ownCloud Developer Manual Release 6.0 The ownCloud developers September 15, 2014

ownCloud Developer Manual
Release 6.0
The ownCloud developers
September 15, 2014
CONTENTS
1
ownCloud 6.0 Developer Documentation
1.1 Index and Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1
2
Development Environment
2.1 Set up web server and database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Get the source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Enabling debug mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
3
3
4
3
App Development
3.1 General . . . . . . . . . . . . . . . . . . . . . . .
3.2 App Development Intro . . . . . . . . . . . . . .
3.3 App Development (ownCloud App API) . . . . .
3.4 App Development (App Framework) . . . . . . .
3.5 Intro . . . . . . . . . . . . . . . . . . . . . . . .
3.6 App Development using ownCloud App API . . .
3.7 App Development using the App Framework App
3.8 Additional APIs . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
23
25
53
124
124
125
127
Core Development
4.1 Translation . . . . . . . . . . . . .
4.2 Unit-Testing . . . . . . . . . . . .
4.3 Theming ownCloud . . . . . . . .
4.4 Creating and activating a new theme
4.5 Structure . . . . . . . . . . . . . .
4.6 How to change images and the logo
4.7 Testing the new theme out . . . . .
4.8 Notes for Updates . . . . . . . . .
4.9 App config . . . . . . . . . . . . .
4.10 OCS Share API . . . . . . . . . . .
4.11 Intro . . . . . . . . . . . . . . . .
4.12 Core related docs . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
129
129
131
133
133
133
134
135
135
135
138
140
140
ownCloud Test Pilots
5.1 Why do you want to join
5.2 Who can join . . . . . .
5.3 How do you join . . . .
5.4 What do you do . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
141
141
141
141
142
4
5
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Bugtracker
143
6.1 Code Reviews on GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
i
6.2
Kanban Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
7
Help and Communication
149
7.1 Mailing lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
7.2 IRC channels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
7.3 Maintainers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
8
Android Application Development
151
8.1 Library Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
9
iOS Application Development
161
9.1 Library Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
10 Indices and tables
181
PHP Namespace Index
183
Index
185
ii
CHAPTER
ONE
OWNCLOUD 6.0 DEVELOPER
DOCUMENTATION
If you want to contribute please read the Contributor agreement
App Development Develop apps for
ownCloud and publish on the
ownCloud appstore
Core Development Develop on the
ownCloud internals
Documentation Create and enhance
documentation
ownCloud Test Pilots Help us to test
ownCloud by joining the testing team
Bugtracker Report, triage or fix
bugs to improve quality
Translation Translate
ownCloud
into your language
Help and Communication Help on
IRC, the mailinglist and forum
iOS Application Development Use
ownCloud in iOS apps
Android Application Development
Use ownCloud in Android
apps
1.1 Index and Tables
• genindex
• modindex
1
ownCloud Developer Manual, Release 6.0
2
Chapter 1. ownCloud 6.0 Developer Documentation
CHAPTER
TWO
DEVELOPMENT ENVIRONMENT
Please follow the steps on this page to set up your development environment.
2.1 Set up web server and database
First set up your web server and database (Section: Manual Installation - Prerequisites).
2.2 Get the source
There are two ways to obtain ownCloud sources:
• Using the stable version
• Using the development version from GitHub which will be explained below.
To check out the source from GitHub you will need to install git (see Setting up git from the GitHub help)
2.2.1 Identify the web server’s directories
To get started the basic git repositories need to be cloned into the web server’s directory. Depending on the distribution
this will either be
• /var/www
• /var/www/html
• /srv/http
2.2.2 Identify the user and group the web server is running as
and the Apache user and group for the chown command will either be
• http
• www-data
• apache
3
ownCloud Developer Manual, Release 6.0
2.2.3 Check out the code
The following commands are using “/var/www” as the web server’s directory and “www-data” as user name and
group.
sudo chmod o+rw /var/www
cd /var/www
git clone https://github.com/owncloud/core.git owncloud
git clone https://github.com/owncloud/apps.git
cd owncloud/
git submodule init
git submodule update
mkdir data
sudo chown -R www-data:www-data config/
sudo chown -R www-data:www-data data/
sudo chown -R www-data:www-data apps/
sudo chmod -R o-rw /var/www
Now restart the web server.
2.2.4 Check out code from additional apps (optional)
If you would like to develop on non-core apps, you can check them out into the apps directory as well. For example
for the calendar, contact and notes apps:
cd /var/www
git clone https://github.com/owncloud/calendar.git
git clone https://github.com/owncloud/contacts.git
git clone https://github.com/owncloud/notes.git
2.2.5 Set up ownCloud
Open http://localhost/owncloud (or the corresponding URL) in your web browser to set up your instance.
2.2.6 Start developing
• App Development
• Core Development
2.3 Enabling debug mode
Note: Do not enable this for production! This can create security problems and is only meant for debugging and
development!
To disable JavaScript and CSS caching debugging has to be enabled in owncloud/config/config.php by
adding this to the end of the file:
DEFINE(’DEBUG’, true);
This is often overwritten after a git pull from core. Always check owncloud/config/config.php afterwards.
4
Chapter 2. Development Environment
CHAPTER
THREE
APP DEVELOPMENT
3.1 General
3.1.1 Security Guidelines
This guideline highlights some of the most common security problems and how to prevent them. Please review your
app if it contains any of the following security holes.
Note: Program defensively: for instance always check for CSRF or escape strings, even if you do not need it. This
prevents future problems where you might miss a change that leads to a security hole.
Note:
All App Framework security features depend on the call of the controller through
OCA\AppFramework\App::main. If the controller method is executed directly, no security checks are
being performed!
SQL Injection
SQL Injection occurs when SQL query strings are concatenated with variables.
To prevent this, always use prepared queries:
<?php
$sql = ’SELECT * FROM ‘users‘ WHERE ‘id‘ = ?’;
$query = \OCP\DB::prepare($sql);
$params = array(1);
$result = $query->execute($params);
If the App Framework is used, write SQL queries like this in the a class that extends the Mapper:
<?php
// inside a child mapper class
$sql = ’SELECT * FROM ‘users‘ WHERE ‘id‘ = ?’;
$params = array(1);
$result = $this->execute($sql, $params);
Cross site scripting
Cross site scripting happens when user input is passed directly to templates. A potential attacker might be able to
5
ownCloud Developer Manual, Release 6.0
inject HTML/JavaScript into the page to steal the users session, log keyboard entries, even perform DDOS attacks on
other websites or other malicious actions.
Despite of the fact that ownCloud uses Content-Security-Policy to prevent the execution of inline JavaScript code
developers are still required to prevent XSS. CSP is just another layer of defense that is not implemented in all web
browsers.
To prevent XSS in your app you have to sanitize the templates and all JavaScripts which performs a DOM manipulation.
Templates
Let’s assume you use the following example in your application:
<?php
echo $_GET[’username’];
An attacker might now easily send the user a link to:
app.php?username=<script src="attacker.tld"></script>
to overtake the user account. The same problem occurs when outputting content from the database or any other
location that is writeable by users.
Another attack vector that is often overlooked is XSS in href attributes. HTML allows to execute javascript in href
attributes like this:
<a href="javascript:alert(’xss’)">
To prevent XSS in your app, never use echo, print() or <%= - use p() instead which will sanitize the input. Also
validate urls to start with the expected protocol (starts with http for instance)!
Note: Should you ever require to print something unescaped, double check if it is really needed. If there is no other
way (e.g. when including of subtemplates) use print_unescaped with care.
If you use the App Framework with Twig templates everything is already escaped by default.
JavaScript
Avoid manipulating the HTML directly via JavaScript, this often leads to XSS since people often forget to sanitize
variables:
var html = ’<li>’ + username + ’</li>"’;
If you really want to use JavaScript for something like this use escapeHTML to sanitize the variables:
var html = ’<li>’ + escapeHTML(username) + ’</li>’;
An even better way to make your app safer is to use the jQuery builtin function $.text() instead of $.html().
DON’T
messageTd.html(username);
DO
6
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
messageTd.text(username);
It may also be wise to choose a proper JavaScript framework like AngularJS which automatically handles the
JavaScript escaping for you.
Clickjacking
Clickjacking tricks the user to click into an invisible iframe to perform an arbitrary action (e.g. delete an user account)
To prevent such attacks ownCloud sends the X-Frame-Options header to all template responses. Don’t remove this
header if you don’t really need it!
This is already built into ownCloud if OC_Template or Twig Templatse are used.
Code executions / File inclusions
Code Execution means that an attacker is able to include an arbitrary PHP file. This PHP file runs with all the privileges
granted to the normal application and can do an enourmous amount of damage.
Code executions and file inclusions can be easily prevented by never allowing user-input to run through the following
functions:
• include()
• require()
• require_once()
• eval()
• fopen()
Note: Also never allow the user to upload files into a folder which is reachable from the URL!
DON’T
<?php
require("/includes/" . $_GET[’file’]);
Note: If you have to pass user input to a potential dangerous, double check to be sure that there is no other way. If it
is not possible otherwise sanitize every user parameter and ask people to audit your sanitize function.
Directory Traversal
Very often developers forget about sanitizing the file path (removing all and /), this allows an attacker to traversal
through directories on the server which opens several potential attack vendors including privilege escalations, code
executions or file disclosures.
DON’T
<?php
$username = OC_User::getUser();
fopen("/data/" . $username . "/" . $_GET[’file’] . ".txt");
DO
3.1. General
7
ownCloud Developer Manual, Release 6.0
<?php
$username = OC_User::getUser();
$file = str_replace(array(’/’, ’\\’), ’’, $_GET[’file’]);
fopen("/data/" . $username . "/" . $file . ".txt");
Note: PHP also interprets the backslash () in paths, don’t forget to replace it too!
Shell Injection
Shell Injection occurs if PHP code executes shell commands (e.g. running a latex compiler). Before doing this, check
if there is a PHP library that already provides the needed functionality. If you really need to execute a command be
aware that you have to escape every user parameter passed to one of these functions:
• exec()
• shell_exec()
• passthru()
• proc_open()
• system()
• popen()
Note: Please require/request additional programmers to audit your escape function.
Without escaping the user input this will allow an attacker to execute arbitary shell commands on your server.
PHP offers the following functions to escape user input:
• escapeshellarg(): Escape a string to be used as a shell argument
• escapeshellcmd(): Escape shell metacharacters
DON’T
<?php
system(’ls ’.$_GET[’dir’]);
DO
<?php
system(’ls ’.escapeshellarg($_GET[’dir’]));
Auth bypass / Privilege escalations
Auth bypass/privilege escalations happens when a user is able to perform not authorized actions.
ownCloud offers three simple checks:
• OCPJSON::checkLoggedIn(): Checks if the logged in user is logged in
• OCPJSON::checkAdminUser(): Checks if the logged in user has admin privileges
• OCPJSON::checkSubAdminUser(): Checks if the logged in user has group admin privileges
8
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Using the App Framework, these checks are already automatically performed for each request and have to be explicitely turned off by using annotations above your controller method, see Controllers.
Additionally always check if the user has the right to perform that action. (e.g. a user should not be able to delete
other users’ bookmarks).
Sensitive data exposure
Always store user data or configuration files in safe locations, e.g. owncloud/data/ and not in the webroot where they
can be accessed by anyone using a webbrowser.
Cross site request forgery
Using CSRF one can trick a user into executing a request that he did not want to make. Thus every POST and GET
request needs to be protected against it. The only places where no CSRF checks are needed are in the main template,
which is rendering the application, or in externally callable interfaces.
Note: Submitting a form is also a POST/GET request!
To prevent CSRF in an app, be sure to call the following method at the top of all your files:
<?php
OCP\JSON::callCheck();
If you are using the App Framework, every controller method is automatically checked for CSRF unless you explicitely
exclude it by setting the @CSRFExemption annotation before the controller method, see Controllers
Unvalidated redirects
This is more of an annoyance than a critical security vulnerability since it may be used for social engineering or
phising.
Always validate the URL before redirecting if the requested URL is on the same domain or an allowed ressource.
DON’T
<?php
header(’Location:’. $_GET[’redirectURL’]);
DO
<?php
header(’Location: http://www.example.com’. $_GET[’redirectURL’]);
Getting help
If you need help to ensure that a function is secure please ask on our mailing list or on our IRC channel #owncloud-dev
on Freenode.
3.1. General
9
ownCloud Developer Manual, Release 6.0
3.1.2 Coding Style & General Guidelines
General
• Maximum line-length of 80 characters
• Use tabs to indent
• A tab is 4 spaces wide
• Opening braces of blocks are on the same line as the definition
• Quotes: ‘ for everything, ” for HTML attributes (<p class=”my_class”>)
• End of Lines : Unix style (LF / ‘n’) only
• No global variables or functions
• Unittests
• Software should work. Only put features into master when they are complete. It’s better to not have a feature
instead of having one that works poorly.
• Regularly reset your installation to see how the first-run experience is like. And improve it.
• When you git pull, always git pull --rebase to avoid generating extra commits like: merged master
into master
• We need a signed contributor agreement from you to commit into the core repository. But no worries; it’s a nice
one. All the information is in our Contributor agreement FAQ.
Userinterface
• Software should get out of the way. Do things automatically instead of offering configuration options.
• Software should be easy to use. Show only the most important elements. Secondary elements only on hover or
via Advanced function.
• User data is sacred. Provide undo instead of asking for confirmation
• The state of the application should be clear. If something loads, provide feedback.
• Do not adapt broken concepts (for example design of desktop apps) just for the sake of consistency. We provide
a better interface.
• Ideally do usability testing to know how people use the software.
• For further UX principles, read Alex Faaborg from Mozilla.
PHP
The ownCloud coding style guide is based on PEAR Coding Standards.
Always use:
<?php
at the start of your php code. The final closing:
?>
should not be used at the end of the file due to the possible issue of sending white spaces.
10
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Comments
All API methods need to be marked with PHPDoc markup. An example would be:
<?php
/**
* Description what method does
* @param Controller $controller the controller that will be transformed
* @param API $api an instance of the API class
* @throws APIException if the api is broken
* @since 4.5
* @return string a name of a user
*/
public function myMethod(Controller $controller, API $api) {
// ...
}
Objects, Functions, Arrays & Variables
Use Pascal case for Objects, Camel case for functions and variables. If you set a default function/method parameter,
do not use spaces. Do not prepend private class members with underscores.
class MyClass {
}
function myFunction($default=null) {
}
$myVariable = ’blue’;
$someArray = array(
’foo’ => ’bar’,
’spam’ => ’ham’,
);
?>
Operators
Use === and !== instead of == and !=.
Here’s why:
<?php
var_dump(0 == "a"); // 0 == 0 -> true
var_dump("1" == "01"); // 1 == 1 -> true
var_dump("10" == "1e1"); // 10 == 10 -> true
var_dump(100 == "1e2"); // 100 == 100 -> true
?>
3.1. General
11
ownCloud Developer Manual, Release 6.0
Control Structures
• Always use { } for one line ifs
• Split long ifs into multiple lines
• Always use break in switch statements and prevent a default block with warnings if it shouldn’t be accessed
<?php
// single line if
if ($myVar === ’hi’) {
$myVar = ’ho’;
} else {
$myVar = ’bye’;
}
// long ifs
if (
$something === ’something’
|| $condition2
&& $condition3
) {
// your code
}
// for loop
for ($i = 0; $i < 4; $i++) {
// your code
}
switch ($condition) {
case 1:
// action1
break;
case 2:
// action2;
break;
default:
// defaultaction;
break;
}
?>
JavaScript
In general take a look at JSLint without the whitespace rules.
• Use a js/main.js or js/app.js where your program is started
• Complete every statement with a ;
• Use var to limit variable to local scope
• To keep your code local, wrap everything in a self executing function. To access global objects or export things
to the global namespace, pass all global objects to the self executing function.
• Use JavaScript strict mode
12
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• Use a global namespace object where you bind publicly used functions and objects to
DO:
// set up namespace for sharing across multiple files
var MyApp = MyApp || {};
(function(window, $, exports, undefined) {
’use strict’;
// if this function or object should be global, attach it to the namespace
exports.myGlobalFunction = function(params) {
return params;
};
})(window, jQuery, MyApp);
DONT (Seriously):
// This does not only make everything global but you’re programming
// JavaScript like C functions with namespaces
MyApp = {
myFunction:function(params) {
return params;
},
...
};
Objects & Inheritance
Try to use OOP in your JavaScript to make your code reusable and flexible.
This is how you’d do inheritance in JavaScript:
// create parent object and bind methods to it
var ParentObject = function(name) {
this.name = name;
};
ParentObject.prototype.sayHello = function() {
console.log(this.name);
}
// create childobject, call parents constructor and inherit methods
var ChildObject = function(name, age) {
ParentObject.call(this, name);
this.age = age;
};
ChildObject.prototype = Object.create(ParentObject.prototype);
// overwrite parent method
ChildObject.prototype.sayHello = function() {
// call parent method if you want to
ParentObject.prototype.sayHello.call(this);
console.log(’childobject’);
};
3.1. General
13
ownCloud Developer Manual, Release 6.0
var child = new ChildObject(’toni’, 23);
// prints:
// toni
// childobject
child.sayHello();
Objects, Functions & Variables
Use Pascal case for Objects, Camel case for functions and variables.
var MyObject = function() {
this.attr = "hi";
};
var myFunction = function() {
return true;
};
var myVariable = ’blue’;
var objectLiteral = {
value1: ’somevalue’
};
Operators
Use === and !== instead of == and !=.
Here’s why:
’’ == ’0’
0 == ’’
0 == ’0’
// false
// true
// true
false == ’false’
false == ’0’
// false
// true
false == undefined
false == null
null == undefined
// false
// false
// true
’ \t\r\n ’ == 0
// true
Control Structures
• Always use { } for one line ifs
• Split long ifs into multiple lines
• Always use break in switch statements and prevent a default block with warnings if it shouldn’t be accessed
DO:
14
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
// single line if
if (myVar === ’hi’) {
myVar = ’ho’;
} else {
myVar = ’bye’;
}
// long ifs
if (
something === ’something’
|| condition2
&& condition3
) {
// your code
}
// for loop
for (var i = 0; i < 4; i++) {
// your code
}
// switch
switch (value) {
case ’hi’:
// yourcode
break;
default:
console.warn(’Entered undefined default block in switch’);
break;
}
CSS
Take a look at the Writing Tactical CSS & HTML video on YouTube.
Don’t bind your CSS too much to your HTML structure and try to avoid IDs. Also try to make your CSS reusable by
grouping common attributes into classes.
DO:
.list {
list-style-type: none;
}
.list > .list_item {
display: inline-block;
}
.important_list_item {
color: red;
}
DON’T:
#content .myHeader ul {
list-style-type: none;
}
3.1. General
15
ownCloud Developer Manual, Release 6.0
#content .myHeader ul li.list_item {
color: red;
display: inline-block;
}
TBD
3.1.3 Debugging
Debug mode
When debug mode is enabled ownCloud, a variety of debugging features are enabled - see debugging documentation.
Add the following to the very end of /config/config.php to enable it:
define( "DEBUG", 1);
Identifying errors
ownCloud uses custom error PHP handling that prevents errors being printed to webserver log files or command line
output. Instead, errors are generally stored in ownCloud’s own log file, located at: /data/owncloud.log
Debugging variables
You should use exceptions if you need to debug variable values manually, and not alternatives like trigger_error()
(which may not be logged).
e.g.:
<?php throw new \Exception( "\$user = $user" ); // should be logged in ownCloud ?>
not:
<?php trigger_error( "\$user = $user" ); // may not be logged anywhere ?>
To disable custom error handling in ownCloud (and have PHP and your webserver handle errors instead), see Debug
mode.
Debugging Javascript
By default all Javascript files in ownCloud are minified (compressed) into a single file without whitespace. To prevent
this, see Debug mode.
Debugging HTML and templates
By default ownCloud caches HTML generated by templates. This may prevent changes to app templates, for example,
from being applied on page refresh. To disable caching, see Debug mode.
Using alternative app directories
It may be useful to have multiple app directories for testing purposes, so you can conveniently switch between different
versions of applications. See the configuration file documentation for details.
16
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
3.1.4 AngularJS
AngularJS is an MV* JavaScript framework by Google.
Documentation is available at these sources:
• Official tutorial
• Developer guide
• API reference
• Screencasts on Youtube
• More screencasts
• Collection of resources
What problem does it solve
jQuery is a nice library but when it comes to building webapplications, one will soon reach a point where its becoming
increasingly impossible to split view and logic.
That problem is caused by jQueries habit to operate directly on dom elements. Most jQuery code looks like this:
$(’.someElement’).doSomething();
That makes it incredible hard to refactor your view because your whole JavaScript code is tightly coupled to the
structure and classes in your HTML.
Another problem is the dynamic generation of DOM elements. You’d normally go with one of these three approaches:
1) Create new element, bind event listeners and append it to the dom:
var $newButton = $(’<button>’).text(’A new button’);
$newButton.click(function(){
alert(’I was clicked!’);
});
$(’.someElement’).append($newButton);
2) Fetch HTML from the server and bind event listeners:
$.post(’/some/url’, function(data){
$newButton = $(data);
$newButton.click(function(){
alert(’I was clicked!’);
});
$(’.someElement’).append($newButton);
});
3) Use jquery templates:
var buttons = [
text: ’A new button’
];
var markup = "<button>${text}</button>";
$.template( "myTemplate", markup );
$.tmpl( "myTemplate", buttons ).appendTo( ".someElement" );
// and bind the click listener
3.1. General
17
ownCloud Developer Manual, Release 6.0
All of the above split the HTML from the original HTML and its hard to bind event listeners (yes, there’s $.on(), but
its slow). You are also in need of updating the DOM by hand.
In contrast to the above solutions, Angular uses XML attributes to define the template logic. This approach does
not only good for your editor, but you’re also less likely to create HTML errors. You can even validate the HTML.
Furthermore, every value that is written into the HTML is escaped to prevent XSS.
Concerning testability: Angular uses Dependency Injection to glue the code together and it’s easy to run your
unittests(look at the examples in the official docs). Angular also ships with mocks for common areas like HTTP
requests or logging.
Thats how the code would look with Angular:
<div ng-app="MyApp" class="someElement" ng-controller="ButtonController">
<button ng-repeat="button in buttons" ng-click="showClicked()">{{ button.text }}</button>
</div>
The button controller handles the complete logic. It would look something like this:
var app = angular.module(’MyApp’, []);
app.controller(’ButtonController’, [’$scope’,
function($scope){
$scope.buttons = [
{text: ’A new button’}
];
$scope.showClicked = function(){
alert(’I was clicked!’);
};
}
);
Now your logic is nicely decoupled from your view and the template logic is where you would expect it to be: in the
HTML markup.
Angular also knows when your data has changed: when a new element is added to the $scope.buttons array, the view
will automatically update. It also updates the view when an existing element in the array changes.
Drawbacks of AngularJS
That brings us also to the biggest problem of AngularJS: It can be slow at times. This is caused by the way Angular
works
Should you somehow require to show more than around 1000 complex elements at once (like 1000 buttons with lots
of wiring inside the code and a ton of attributes) there will most likely be performance problems (To be fair: normal
JavaScript would also run into performance problems).
One way to tackle this is to use autopaging (progressive loading) that only renders X elements and loads the next batch
when the user scrolls down for instance. This also reduces the traffic. Software that successfully uses this approach is
Google Reader for instance.
When porting the News app to AngularJS we found that the benefits outweighed the drawbacks and that we could
optimize the Code well enough to offer a good user experience.
But all in all you should build an optimized prototype and compare it to a non angular app to make sure that the user
experience is good.
18
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
3.1.5 Dependency Injection
Dependency Injection is a programming pattern that helps you decouple dependencies between classes. The result is
cleaner and more testable code. A good overview over how it works and what the benefits are can be read on Google’s
Clean Code Talks
Basic Problem
Consider the following class:
<?php
class PersonController {
public function listNames(){
$sql = "SELECT ‘prename‘ FROM ‘persons‘";
$query = \OCP\DB::prepare($sql);
$result = $query->execute();
while($row = $result->fetchRow()){
echo ’<p>’ . htmlentities($row[’prename’]) . ’</p>’;
}
}
}
This class prints out all prenames of a person and is called by using:
<?php
$controller = new PersonController();
$controller->listNames();
This looks like clean code until the first tests are written. Tests are absolutely necessary in every application! Do
not think that your app is too small to require them. The code will eventuell grow bigger and will have to be
refactored.
If code is refactored code will be written. If code is being written there will be bugs. If there will be bugs then every
possible failure must be tested. This is tiresome and must be automated.
If the code already comes with tests, this is not a problem. The unittests ensure that the changes did not introduce
regressions.
Back to the above example: How would a test for the SQL query look like?
• \OCP\DB needs to be Monkey Patched to make the query accessible
• The monkey patching must not conflict with other tests which use the same class
• There must be a database connection or the test will fail
• If data is inserted into the database, it needs to be deleted afterwards
This is a significant amount of work that needs to be done for every test. If something is hard to do, people tend to not
do it that often or even won’t do it at all. Because tests are necessary they need to be written and therefore they must
be as easy as possible.
Furthermore Monkey Patching is not a good solution, so most static classes need to be built with testing methods built
in. These methods are only for debugging purposes and do not add any value to the running product.
3.1. General
19
ownCloud Developer Manual, Release 6.0
Inject Dependencies
The reason why the example class is hard to test is because it depends on \OCP\DB. To be able to test it, the class
needs to be replaced with a mock object.
This mock object is passed to the class either using a Setter or using an additional parameter in the constructor. The
most common one is constructor injection.
Using constructor injection, the example can be refactored by simply passing the object which executes the database
request. At this point the first problem arises: \OCP\DB uses static methods and can not be instatiated as an object.
This is because ownCloud uses static incorrectly.
Note: Static methods and attributes should only be used for sharing information that needs to be available in all
classes of this instance. Do not use static methods for utility functions! Instantiating an object is only one line in
your code and can limitted to one place by putting it into a container.
Note: Because of constructor injection, the constructor should not contain anything else than initializing attributes.
Never open a connection in a constructor. Provide a seperate method that handles resourceintensive initialization.
To get around the the static method call, which is actually more like a function call, the method needs to be wrapped
in an object. This object can be passed into the class. The refactored class would look like this:
<?php
class API {
public function prepareQuery($sql){
return \OCP\DB::prepare($sql);
}
}
class PersonController {
private $api;
public function __construct($api){
$this->api = $api;
}
public function listNames(){
$sql = "SELECT ‘prename‘ FROM ‘persons‘";
$query = $this->api->prepareQuery($sql);
$result = $query->execute();
while($row = $result->fetchRow()){
echo ’<p>’ . htmlentities($row[’prename’]) . ’</p>’;
}
}
}
// run controller
$api = new API();
$controller = new PersonController($api);
$controller->listNames();
Now a first, simple test can be written:
20
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Note: The other methods that are called on the mock object need to be implemented too, but for the sake of simplicity
this is not done in this example
<?php
class PersonControllerTest extends \PHPUnit_Framework_TestCase {
private $api;
public function setUp(){
$this->api = $this->getMock(’API’, array(’prepareQuery’));
$this->controller = new PersonController($this->api);
}
public function testListNamesQuery(){
$sql = "SELECT ‘prename‘ FROM ‘persons‘";
$this->api->expects($this->once())
->method(’prepareQuery’)
->with($this->equalTo($sql));
$this->controller->listNames();
}
}
Limit input and output to one place
The code also depends on another function: echo. Because this is usually hard to test, it is better to limit the use of
input and output functions to one place. Remember that ownCloud uses PHP which likes to do output in functions like
header or session_start. The refactored code would look like this:
<?php
class API {
public function prepareQuery($sql){
return \OCP\DB::prepare($sql);
}
}
class PersonController {
private $api;
public function __construct($api){
$this->api = $api;
}
public function listNames(){
$sql = "SELECT ‘prename‘ FROM ‘persons‘";
$query = $this->api->prepareQuery($sql);
$result = $query->execute();
$output = ’’;
3.1. General
21
ownCloud Developer Manual, Release 6.0
while($row = $result->fetchRow()){
$output .= ’<p>’ . htmlentities($row[’prename’]) . ’</p>’;
}
return $output;
}
}
// run controller
$api = new API();
$controller = new PersonController($api);
echo $controller->listNames();
The output test can now be implemented as a simple string comparison.
Use a container
The above example works fine in small cases, but if the class depends on four other classes that each depend on two
other classes there will be eight instantiations. Also if one constructor parameter changes, every line that instantiates
the class will have to change too.
The solution is to define the injected classes as dependencies and let the system handle the rest.
Pimple is a simple container implementation. The documentation on how to use it can be read on the Pimple Homepage
The dependencies can now be defined like this:
<?php
class DIContainer extends \Pimple {
public function __construct(){
$this[’API’] = $this->share(function($c){
return new API();
});
$this[’PersonController’] = function($c){
return new PersonController($c[’API’]);
};
}
The output could look like this:
<?php
$container = new DIContainer();
echo $container[’PersonController’]->listNames();
The container figures out all dependencies and instantiates the objects accordingly. Also by using the share method,
the anti-pattern Singleton can be avoided. From the Pimple Tutorial:
By default, each time you get an object, Pimple returns a new instance of it. If you want the same in
22
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Do not inject the container
Injecting the container as a dependency is known as the Service Locator Pattern which is widely regarded as an antipattern.
It makes your code dependant on the container and hides the class’ real dependencies. This makes testing and maintaining harder.
3.2 App Development Intro
3.2.1 Creating An App
After the apps were cloned into either
• /var/www
• /var/www/html
• /srv/http
Depending on the used distribution change into that directory inside a terminal:
cd /var/www/
Create the app automatically
The scaffolding script provides an easy way to generate boilerplate code for an app. To install the tool, install Python
3 and python-pip, then run:
sudo pip install owncloud_scaffolding
To create an app using the App Framework run:
owncloud.py startapp myapp
To create a standard ownCloud app run:
owncloud.py startapp --classic myapp
This will create all the needed files in the current directory. For more information on how to customize the generated
app, see the GitHub page
Create the app manually
If you dont want to use the scaffolding tool, heres how you create all the needed files: create a directory for the app
and make it writable:
mkdir apps/YOUR_APP
sudo chown -R YOUR_USER:users apps/YOUR_APP
If you are unsure about your user whoami to find out your user:
whoami
3.2. App Development Intro
23
ownCloud Developer Manual, Release 6.0
Create basic files
The following files need to be created: appinfo/info.xml, appinfo/version and appinfo/app.php. To
do that change into the directory of your app:
cd apps/YOUR_APP
mkdir appinfo
touch appinfo/version appinfo/app.php appinfo/info.xml
Set app version
To set the version of the app, simply write the following into appinfo/version:
0.1
Create metadata
ownCloud has to know what your app is. This information is located inside the appinfo/info.xml:
<?xml version="1.0"?>
<info>
<id>appname</id>
<name>My App</name>
<description>Simple app that does some computing</description>
<version>1.0</version>
<licence>AGPL</licence>
<author>Your Name</author>
<require>5</require>
</info>
For more information on the content of appinfo/info.xml see: App Metadata
Enable the app
The easiest way to enable is to symlink it into the owncloud/apps directory:
ln -s /var/www/apps/YOUR_APP /var/www/owncloud/apps/
This is also how other apps from the apps directory have to be enabled. A second way is to tell ownCloud about the
directory. Use App config to set up multiple app directories.
The app can now be enabled on the ownCloud apps page.
Note: The app does not show up yet in the navigation. This is intended. How to create an entry in the navigation is
explained in the following tutorials.
Start coding
The basic files are now in place and the app is enabled. There are two ways to create the app:
• Use the ownCloud app API
• Use the App Framework app
24
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
If you are new to programming and want to create an app fast you migth want to use the ownCloud app API, if you
are an advanced programmer or used to frameworks you might want to use the App Framework App.
App Framework Comparison
To simplify the decision see this comparison chart:
Criteria
Difficulty
Architecture
Testability
Maintainability
Templates
Security
ownCloud app API
easy
routes and templates
hard
hard
OC_Template
manual checks
App Framework
medium
routes and MVC
easy: built-in Dependency Injection and TDD tools
easy
OC_Template and Twig
escapse XSS (Twig only), does CSRF and Authentication checks by default
3.3 App Development (ownCloud App API)
3.3.1 App Tutorial
This tutorial contains the traditional approach to write an app and continues where Creating An App left off. The result
will be a simple “Hello World” app.
Navigation entry
This file will always loaded for every app and can for instance be used to load additional JavaScript for the files app.
Therefore the navigation entry has to be registered in this file.
appinfo/app.php:
<?php
\OCP\App::addNavigationEntry(array(
// the string under which your app will be referenced in owncloud
’id’ => ’myapp’,
// sorting weight for the navigation. The higher the number, the higher
// will it be listed in the navigation
’order’ => 74,
// the route that will be shown on startup
’href’ => \OCP\Util::linkToRoute(’myapp_index’),
// the icon that will be shown in the navigation
// this file needs to exist in img/example.png
’icon’ => \OCP\Util::imagePath(’myapp’, ’nav-icon.png’),
// the title of your application. This will be used in the
// navigation or on the settings page of your app
’name’ => ’My App’
));
3.3. App Development (ownCloud App API)
25
ownCloud Developer Manual, Release 6.0
Create the main route
Routes map the URL to functions and allow to extract values. To show the content when the navigation entry is clicked,
the index route which was defined in the appinfo/app.php needs to be created:
appinfo/routes.php:
<?php
$this->create(’myapp_index’, ’/’)->action(
function($params){
require __DIR__ . ’/../index.php’;
}
);
Write the logic
In this example the logic is written procedurally in a PHP file. This file contains database queries and security checks
and prints the final template:
index.php:
<?php
// Look up other security checks in the docs!
\OCP\User::checkLoggedIn();
\OCP\App::checkAppEnabled(’myapp’);
$tpl = new OCP\Template("myapp", "main", "user");
$tpl->assign(’msg’, ’Hello World’);
$tpl->printPage();
Create the template
The last thing that needs to be done is to create the Templates file which was used in the index.php.
templates/main.php:
<p><?php p($_[’msg’]); ?></p>
Congratulations! The message “Hello World” can now be seen on the main page of your app. .. 6.0 replace:: 6.0
3.3.2 App Metadata
The appinfo/info.xml contains metadata about the app:
<?xml version="1.0"?>
<info>
<id>yourappname</id>
<name>Your App</name>
<description>Your App description</description>
<version>1.0</version>
<licence>AGPL</licence>
<author>Your Name</author>
<require>5</require>
26
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
<types>
<type>filesystem</type>
</types>
<remote>
<file id="caldav">appinfo/caldav.php</file>
</remote>
<documentation>
<user>http://doc.owncloud.org</user>
<admin>http://doc.owncloud.org</admin>
</documentation>
<website>http://www.owncloud.org</website>
<public>
<file id="caldav">appinfo/caldav.php</file>
</public>
<standalone />
<default_enable />
<shipped>true</shipped>
</info>
id
Required: This field contains the internal app name, and has to be the same as the foldername of the app. This id
needs to be unique in ownCloud, meaning no other app should have this id.
name
Required: This is the human-readable name/title of the app that will be displayed in the app overview page.
description
Required: This contains the description of the app which will be shown in the apps overview page.
version
Contains the version of your app. Please also provide the same version in the appinfo/version.
licence
Required: The licence of the app. This licence must be compatible with the AGPL and must not be proprietary, for
instance:
• AGPL 3 (recommended)
• MIT
If a proprietary/non AGPL compatible licence should be used, the ownCloud Enterprise Edition must be used.
3.3. App Development (ownCloud App API)
27
ownCloud Developer Manual, Release 6.0
author
Required: The name of the app author or authors.
require
Required: The minimal version of ownCloud.
types
ownCloud allows to specify four kind of “types”. Currently supported “types”:
• prelogin: apps which needs to load on the login page
• filesystem: apps which provides filesystem functionality (e.g. files sharing app)
• authentication: apps which provided authentication backends
• logging: apps which implement a logging system
public
Used to provide a public interface (requires no login) for the app.
cloud/index.php/public. Example with id set to ‘calendar’:
The id is appended to the URL /own-
/owncloud/index.php/public/calendar
Also take a look at External API.
remote
Same as public but requires login. The id is appended to the URL /owncloud/index.php/remote. Example with id set
to ‘calendar’:
/owncloud/index.php/remote/calendar
Also take a look at External API.
documentation
link to ‘admin’ and ‘user’ documentation
website
link to project webpage
standalone
Can be set to true to indicate that this app is a webapp. This can be used to tell GNOME Web for instance to treat this
like a native application.
28
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
default_enable
Core apps only: Used to tell ownCloud to enable them after the installation.
shipped
Core apps only: Used to tell ownCloud that the app is in the standard release.
Please note that if this attribute is set to FALSE or not set at all, every time you disable the application, all the files of
the application itself will be REMOVED from the server!
3.3.3 Classloader
The classloader is provided by ownCloud and loads all your classes automatically. The only thing left to include
by yourself are 3rdparty libraries. Note that this means that the classes need to be named and organized in folders
according to their full qualifier.
The classloader works like this:
• Take the full qualifier of a class:
\OCA\AppTemplateAdvanced\Db\ItemMapper
• If it starts with \OCA include file from the apps directory
• Cut off \OCA:
\AppTemplateAdvanced\Db\ItemMapper
• Convert all charactes to lowercase:
\apptemplateadvanced\db\itemmapper
• Replace \ with /:
/apptemplateadvanced/db/itemmapper
• Append .php:
/apptemplateadvanced/db/itemmapper.php
• Prepend /apps because of the OCA namespace and include the file:
require ’/apps/apptemplateadvanced/db/itemmapper.php’;
Remember:
for it to be autoloaded, the itemmapper.php needs to either be stored in the
/apps/apptemplateadvanced/db/ folder, or adjust its namespace according to the folder it’s stored in. .. 6.0
replace:: 6.0
3.3.4 Routes
PHP usually treats the URL like a filepath. This is easy for beginners but gets more complicated if a good architecture
is required. For instance if an URL should call a certain function/method or if values should be extracted from the
URL.
Routing connects your URLs with your controller methods and allows you to create constant and nice URLs. Its also
easy to extract values from the URLs.
3.3. App Development (ownCloud App API)
29
ownCloud Developer Manual, Release 6.0
ownCloud uses Symphony Routing
Routes are declared in appinfo/routes.php
A simple route would look like this:
<?php
// this route matches /index.php/yourapp/myurl/SOMEVALUE
$this->create(’yourappname_routename’, ’/myurl/{key}’)->action(
function($params){
require __DIR__ . ’/../index.php’;
}
);
The first argument is the name of your route. This is used as an identifier to get the URL of the route and is a nice
way to generate the URL in your templates or JavaScript for certain links since it does not force you to hardcode your
URLs.
Note: The identifier should always start with the appid since they are global and you could overwrite a route of a
different app
The second parameter is the URL which should be matched. You can extract values from the URL by using {key} in
the section that you want to get. That value is then available under $params[’key’], for the above example it would
be $params[’key’]. You can omit the parameter if you dont extract any values from the URL at all.
If a default value should be used for an URL parameter, it can be set via the defaults method:
<?php
$this->create(’yourappname_routename’, ’/myurl/{key}’)->action(
function($params){
require __DIR__ . ’/../index.php’;
}
)->defaults(’key’ => ’john’);
The action method allows you to register a callback which gets called if the route is matched. You can use this to call
a controller or simply include a PHP file.
Using routes in templates and JavaScript
To use routes in OC_Template, use:
<?
print_unescaped(\OCP\Util::linkToRoute( ’yourappname_routename’, array(’key’ => 1)));
In JavaScript you can get the URL for a route like this:
var params = {key: 1};
var url = OC.Router.generate(’yourappname_routename’, params);
console.log(url); // prints /index.php//yourappname/myurl/1
Note: Be sure to only use the routes generator after the routes are loaded. This can be done by registering a callback
with OC.Router.registerLoadedCallback(callback)
30
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
3.3.5 Database Schema
ownCloud uses a database abstraction layer on top of either MDB2 or PDO, depending on the availability of PDO on
the server.
The database schema is inside appinfo/database.xml in MDB2’s XML scheme notation where the placeholders
*dbprefix* (*PREFIX* in your SQL) and *dbname* can be used for the configured database table prefix and database
name.
An example database XML file would look like this:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<table>
<name>*dbprefix*yourapp_items</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>user</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>name</name>
<type>text</type>
<notnull>true</notnull>
<length>100</length>
</field>
<field>
<name>path</name>
<type>clob</type>
<notnull>true</notnull>
</field>
</declaration>
</table>
</database>
To update the tables used by the app, simply adjust the database.xml file and increase the app version number in
appinfo/version to trigger an update.
3.3.6 Database Access
After the schema has been defined it is possible to query the database. ownCloud uses prepared statements. A simple
query would look like this:
3.3. App Development (ownCloud App API)
31
ownCloud Developer Manual, Release 6.0
<?php
// *PREFIX* is being replaced with the ownCloud installation prefix
$sql = ’SELECT * FROM ‘*PREFIX*myusers‘ WHERE id = ?’;
$args = array(1);
$query = \OCP\DB::prepare($sql);
$result = $query->execute($args);
while($row = $result->fetchRow()) {
$userName = $row[’username’];
}
If a new element is saved to the database the inserted id can be accessed by using:
<?php
$id = \OCP\DB::insertid();
It is also possible to use transactions:
<?php
\OCP\DB::beginTransaction();
$sql = ’SELECT * FROM ‘*PREFIX*myusers‘ WHERE id = ?’;
$args = array(1);
$query = \OCP\DB::prepare($sql);
$result = $query->execute($args);
\OCP\DB::commit();
3.3.7 Templates
ownCloud provides its own templating system.
In every template file you can easily access the template functions listed in OC Templates. To access the assigned variables in the template, use the $_[] array. The variable will be availabe under the key that you defined (e.g. $_[’key’]).
templates/main.php
<?php foreach($_[’entries’] as $entry){ ?>
<p><?php p($entry); ?></p>
<?php
}
print_unescaped($this->inc(’sub.inc’));
?>
Warning: Changed in version 5.0. To prevent XSS the following PHP functions for printing are forbidden:
echo, print() and <?=. Instead use the p() function for printing your values. Should you require unescaped
printing, double check for XSS and use: print_unescaped.
Templates can also include other templates by using the $this->inc(‘templateName’) method. Use this if you find
yourself repeating a lot of the same HTML constructs.
32
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
The parent variables will also be available in the included templates, but should you require it, you can also pass new
variables to it by using the second optional parameter as array for $this->inc.
templates/sub.inc.php
<div>I am included but i can still access the parents variables!</div>
<?php p($_[’name’]); ?>
<?php print_unescaped($this->inc(’other_template’, array(’variable’ => ’value’))); ?>
For more info, see OC Templates .. 6.0 replace:: 6.0
3.3.8 Static content
Static content consists of:
• img/: all images
• js/: all JavaScript files
• css/: all CSS files
Because ownCloud templates do not support template inheritance it is not possible to add your own
script tags. Therefore ownCloud provides the methods \OCPUtil::addScript(‘your_app_id’, ‘script’) and
\OCPUtil::addStyle(‘your_app_id’, ‘style’). The first parameter is the app’s id, the second one is the path relative to the js/ respectively the css/ folder without the file extension.
If you use Twig Templates, there is the script and style function, see Templates.
CSS and JavaScript are compressed by ownCloud so if the CSS or JavaScript do not seem to get updated, check if the
debug mode is enabled. To enable it see Enabling debug mode
3.3.9 CSS
Stylesheets have to be included in your templates, see Static content
If you have to include an image or css file in your CSS, prepend the following to your path:
• %appswebroot%: gets the absolute path to your app
• %webroot%: gets the absolute path to owncloud
For example:
.folder > .title {
background-image: url(’%webroot%/core/img/places/folder.svg’);
}
CSS for apps
ownCloud comes with special CSS rules for apps to make app development easier.
Todo
document this
3.3. App Development (ownCloud App API)
33
ownCloud Developer Manual, Release 6.0
Formfactors
ownCloud automatically detects what kind of form factor you are using.
Currently supported are:
• mobile: works well on mobiles
• tablet: optimized for devices like iPads or Android Tablets
• standalone: mode where only the content of an App is shown. The header, footer and side navigation is not
visible. This is useful if ownCloud is embedded in other applications.
The auto detection can be overwritten by using the “formfactor” GET variable in the url:
index.php/myapp?formfactor=mobile
If you want to provide a different stylesheet or javascript file for mobile devices just suffix the formfactor in the
filename, like:
style.mobile.css
or:
script.tablet.css
3.3.10 External API
Introduction
The external API inside ownCloud allows third party developers to access data provided by ownCloud apps. ownCloud
version 5.0 will follow the OCS v1.7 specification (draft).
Usage
Registering Methods
Methods are registered inside the appinfo/routes.php using OCP\API
<?php
\OCP\API::register(
’get’,
’/apps/yourapp/url’,
function($urlParameters) {
return new \OC_OCS_Result($data);
},
’yourapp’,
\OC_API::ADMIN_AUTH
);
Returning Data
Once the API backend has matched your URL, your callable function as defined in $action will be executed. This
method is passed as array of parameters that you defined in $url. To return data back the the client, you should return
an instance of OC_OCS_Result. The API backend will then use this to construct the XML or JSON response.
34
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Authentication & Basics
Because REST is stateless you have to send user and password each time you access the API. Therefore running
ownCloud with SSL is highly recommended otherwise everyone in your network can log your credentials:
https://user:[email protected]/ocs/v1.php/apps/yourapp
Output
The output defaults to XML. If you want to get JSON append this to the URL:
?format=json
Output from the application is wrapped inside a data element:
XML:
<?xml version="1.0"?>
<ocs>
<meta>
<status>ok</status>
<statuscode>100</statuscode>
<message/>
</meta>
<data>
<!-- data here -->
</data>
</ocs>
JSON:
{
"ocs": {
"meta": {
"status": "ok",
"statuscode": 100,
"message": null
},
"data": {
// data here
}
}
}
Statuscodes
The statuscode can be any of the following numbers:
• 100 - successfull
• 996 - server error
• 997 - not authorized
• 998 - not found
• 999 - unknown error
3.3. App Development (ownCloud App API)
35
ownCloud Developer Manual, Release 6.0
3.3.11 Filesystem
ownCloud handling of filesystems is very flexible. A variety of local and remote filesystem types are supported, as
well as a variety of hooks and optional features such as encryption and version control. It is important that apps use
the correct methods for interacting with files in order to maintain this flexibility.
In some cases using PHP’s internal filesystem functions directly will be sufficient, such as unlink() and mkdir(). Most
of the time however it is necessary to use one of ownCloud’s filesystem classes. This documentation assumes that you
are working with files stored within a user’s directory (as opposed to ownCloud core files), and therefore need to use
OC\Files\View.
Todo
write the rest
3.3.12 Hooks
In ownCloud apps, function or methods (event handlers) which are used by the app and called by ownCloud core
hooks, are generally stored in apps/appname/lib/hooks.php. Hooks are a way of implementing the observer
pattern, and are commonly used by web platform applications to provide clean interfaces to third party applications
which need to modify core application functionality.
In ownCloud, a hook is a function whose name can be used by developers of plug-ins to ensure that additional code
is executed at a precise place during the execution of other parts of ownCloud code. For example, when an ownCloud
user is deleted, the ownCloud core hook post_deleteUser is executed. In the calendar app’s appinfo/app.php,
this hook is connected to the app’s own event handler deleteUser (user here refers to an ownCloud user; deleteUser
deletes all addressbooks for that a given ownCloud user).
When post_deleteUser calls the calender app’s deleteUser event handler, it supplies it with an argument, which is an
array containing the user ID of the user that has just been deleted. This user ID is then used by the event handler to
specify which address books to delete. There are three components to the use of hooks in this example:
1. The ownCloud core hook post_deleteUser, (see what arguments / data it will provide in lib/user.php,
where it is defined)
2. The event handler deleteUser, defined in apps/contacts/lib/hooks.php.
3. The connection of the hook to the event handler, in apps/contacts/appinfo/app.php.
Hook Terminology
• Signal class / emitter class: the class that contains the method which contains the creation of the hook (and a
call to the emit() method) e.g. OC_User
• Signal / signal name: the name of the hook, e.g. post_deleteUser
• Slot class: class housing the event handling method, e.g. OC_Contacts_Hooks
• Slot name: event handler method, e.g. deleteUser (function that deletes all contact address books for a user)
Available hooks
File: apps/calendar/ajax/events.php, Class: OC_Calendar
• Hook: getEvents
36
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
File: apps/calendar/index.php, Class: OC_Calendar
• Hook: getSources
File: dav.php, Class: OC_DAV
• Hook: initialize
File: lib/migrate.php, Class: OC_User
• Hook: pre_createUser
• Hook: post_createUser
File: lib/filecache.php, Class: OC_Filesystem
• Hook: post_write
• Hook: post_write
• Hook: post_delete
• Hook: post_write
File: lib/user.php, Class: OC_User
• Hook: pre_createUser
• Hook: post_createUser
• Hook: pre_deleteUser
• Hook: post_deleteUser
• Hook: pre_login
• Hook: post_login
• Hook: logout
• Hook: pre_setPassword
• Hook: post_setPassword
File: lib/group.php, Class: OC_Group
• Hook: pre_createGroup
• Hook: post_createGroup
• Hook: pre_deleteGroup
• Hook: post_deleteGroup
• Hook: pre_addToGroup
• Hook: post_addToGroup
• Hook: pre_removeFromGroup
• Hook: post_removeFromGroup
3.3.13 Data Migration
As of OC4, user migration is supported. To include migration support in your app (which is highly recommended and
does not take long) you must provide a appinfo/migrate.php. The function of the migrate.php file is to provide
an import and export functions for app data. To assist in this, we set the user id of the user being exported / user being
3.3. App Development (ownCloud App API)
37
ownCloud Developer Manual, Release 6.0
imported in $this->uid. There is also an instance of the OC_Migration_Content class stored in $this->content. The
OC_Migration_Content class helps to make importing and exporting data easy for app developers.
Export
In this function, you must do everything necessary to export a user from the current ownCloud instance, given a user
id. For most apps this is just the case of saving a few rows from the database.
Database Data
To make exporting database data really easy, the class OC_Migration_Content has a method called copyRows()
which will save these rows for you given some options. Take a look at the export function for the bookmarks app:
<?php
function export( ){
OC_Log::write(’migration’,’starting export for bookmarks’,OC_Log::INFO);
// migrate two tables
$bookmarkOptions = array(
’table’=>’bookmarks’,
’matchcol’=>’user_id’,
’matchval’=>$this->uid,
’idcol’=>’id’
);
$bookmarkIds = $this->content->copyRows( $bookmarkOptions );
$bookmarkTagsOptions = array(
’table’=>’bookmarks_tags’,
’matchcol’=>’bookmark_id’,
’matchval’=>$ids
);
$bookmarkTagsIds = $this->content->copyRows( $bookmarkTagsOptions );
// If both returned some ids then they worked
if( is_array( $bookmarkIds ) && is_array( $bookmarkTagsIds ) )
{
return true;
} else {
return false;
}
}
The bookmarks app stores all of its data in the database, in two tables: *PREFIX*bookmarks and *PREFIX*bookmarks_tags so to export this, we need to run copyRows() twice. Here is an explanation of the options
passed to it:
• table: string name of the table to export (without any prefix)
• matchcol: (optional) string name of the column that will be matched with the value in matchval (Basically the
column used in the WHERE sql query)
• matchval: (optional) the value that will be searched for in the table
• idcol: the name of the column that will be returned
To export the bookmarks, matchcol is set to the user_id column and matchval is set to the user being exported: $this>content->uid. idcol is set to the id of the bookmark, as we need to retrive the tags associated with the bookmarks for
38
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
the user being exported. The function will return an array of ids.Next we run the copyRows() method again, this time
on the bookmarks_tags table, matching a range of values (as we want to find all tags, related to all bookmarks owned
by the exported user).Finally we check that both functions returned arrays which confirms that they were successful
and return a boolean value to represent the success of the export.
Files
If you use files to hold some app data in data/userid/appname/, they will be automatically copied exported for you.
Import
Import is a little more tricky as we have to take into account data from different versions of your app, and also handle
changing primary keys. Here is the import function for the bookmarks app which imports bookmarks and tags:
<?php
function import(){
switch( $this->appinfo->version ){
default:
// All versions of the app have had the same db structure
// so all can use the same import function
$query = $this->content->prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" );
$results = $query->execute( array( $this->olduid ));
$idmap = array();
while( $row = $results->fetchRow() ){
// Import each bookmark, saving its id into the map
$sql = "INSERT INTO *PREFIX*bookmarks" .
"(url, title, user_id, public, added, lastmodified)" .
" VALUES (?, ?, ?, ?, ?, ?)";
$query = OC_DB::prepare($sql);
$query->execute( array(
$row[’url’],
$row[’title’],
$this->uid,
$row[’public’],
$row[’added’],
$row[’lastmodified’]
) );
// Map the id
$idmap[$row[’id’]] = OC_DB::insertid();
}
// Now tags
foreach($idmap as $oldid => $newid){
$sql = "SELECT * FROM bookmarks_tags WHERE user_id LIKE ?";
$query = $this->content->prepare($sql);
$results = $query->execute( array( $oldid ) );
while( $row = $data->fetchRow() ){
// Import the tags for this bookmark, using the new bookmark id
$sql = "INSERT INTO *PREFIX*bookmarks_tags(bookmark_id, tag)".
" VALUES (?, ?)";
$query = OC_DB::prepare($sql);
$query->execute( array( $newid, $row[’tag’] ) );
}
}
// All done!
break;
3.3. App Development (ownCloud App API)
39
ownCloud Developer Manual, Release 6.0
}
return true;
}
We start off by using a switch to run different import code for different versions of your app. $this->appinfo->version
contains the version string from the appinfo/info.xml of your app. In the case of the bookmarks app the db
structure has not changed, so only one version of import code is needed.
To import the db data, first we must retrive it from the migration.db. To do this we use the prepare method from
OC_Migration_Content, which returns a MDB2 db object. We then cycle through the bookmarks in migration.db
and insert them into the owncloud database. The important bit is the idmapping. After inserting a boookmark, The
new id of the bookmark is saved in an array, with the key being the old id of the bookmark. This means when inserting
the tags, we know what the new id of the bookmark is simply by getting the value of $idmap[’oldid’].
Remember this part of the import code may be a good place to emit some hooks depending on your app. For example
the contacts app could emit some hooks to show some contacts have been added.
After importing the bookmarks, we must import the tags. It is a very similar process to importing the bookmarks,
except we have to take into account the changes in primary keys. This is done by using a foreach key in the $idmap
array, and then inserting the tags using the new id.
After all this, we must return a boolean value to indicate the success or failure of the import. Again, app data files
stored in data/userid/appname/ will be automatically copied over before the apps import function is executed, this
allows you to manipulate the imported files if necessary.
Conclusion
To fully support user migration for your app you must provide an import and export function under an instance of
OC_Migration_Provider and put this code in the file appinfo/migrate.php
3.3.14 ownCloud API
OCS
Manages the backend of the external API
class OCP\API
register($method, $url, $action, $app, $authlevel, $defaults, $requirements)
Registers an API route with the backend.
Parameters
• $method (string) – The HTTP method (get, post, put or delete)
• $url (string) – The URL that defines the API route which can also include params (See
the Symfony Documentation)
• $action (callable) – The function to call
• $app (string) – The app id
• $authlevel (int) – The required level of authentication to access the API
method.
The following constants can be passed: OC_API::ADMIN_AUTH,
OC_API::SUBADMIN_AUTH, OC_API::USER_AUTH, OC_API::GUEST_AUTH
• $defaults (array) – associative array of defaults for the URL parameters. Keys are the
parameter names as defined in the url
40
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• $requirements (array) – associative array of requirements for the url parameters (See the
Symfony Documentation: Adding Requirements)
OCS_Result
Holds data on the result of the API method.
class OC_OCS_Result
__construct($data=null, $code=100, $message=null)
Creates an OC_OCS_Result object
Parameters
• $data (mixed) – The data you wish to return, this may be a string, integer or array
• $code (int) – The response code you wish to return, defaults to success (100)
• $message (string) – An optional message to return with the response (explaining the result)
setTotalItems($items)
Sets the <totalitems> value in the response. Use this to inform the client of the range of data available.
Parameters
• $items (int) – The number of items in the full data set
setItemsPerPage($items)
Sets the <itemsperpage> value in the response. Use this to inform the client of the number of pieces of
data per page.
Parameters
• $items (int) – The number of items per page of data.
OC Templates
OC_Template
class OC_Template
This class provides the templates for owncloud. It is used for loading template files, assign variables to it and
render the whole template.
__construct($app, $name[, $renderas ])
Parameters
• $app (string) – the name of the app
• $file (string) – name of the template file (without suffix)
• $renderas (string) – If $renderas is set, OC_Template will try to produce a full page in
the according layout. For now, renderas can be set to “guest”, “user” or “admin”
Returns OC_Template object
Example:
3.3. App Development (ownCloud App API)
41
ownCloud Developer Manual, Release 6.0
<?php
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
?>
addHeader($tag, $attributes[, $text=’‘ ])
Parameters
• $tag (string) – tag name of the element
• $attributes (array) – array of attrobutes for the element
• $text (string) – the text content for the element
Add a custom element to the html <head>
Example:
<?php
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
$mainTemplate->addHeader(’title’, array(), ’My new Page’);
?>
append($key, $value)
Parameters
• $key (string) – the key under which the variable can be accessed in the template
• $value – the value that we want to pass
Returns bool
This function assigns a variable in an array context. If the key already exists, the value will be appended.
It can be accessed via $_[$key][$position] in the template.
Example:
<?php
$customers = array("john", "frank");
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
$mainTemplate->assign(’customers’, $customers);
$mainTemplate->append(’customers’, ’hanna’);
?>
assign($key, $value)
Parameters
• $key (string) – the key under which the variable can be accessed in the template
• $value – the value that we want to pass
Returns bool
This function assigns a variable. It can be accessed via $_[$key] in the template. If the key existed before,
it will be overwritten
Example:
<?php
$customers = array("john", "frank");
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
42
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
$mainTemplate->assign(’customers’, $customers);
?>
detectFormfactor()
Returns The mode of the client as a string. default -> the normal desktop browser interface,
mobile -> interface for smartphones, tablet -> interface for tablets, standalone -> the default
interface but without header, footer and sidebar, just the application. Useful to use just a
specific app on the desktop in a standalone window.
Example:
<?php
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
$formFactor = $mainTemplate->detectFormfactor();
?>
fetchPage()
Returns the HTML of the template as string
This function proceeds the template and but prints no output.
Example:
Todo
provide example
getFormFactorExtension()
Returns Returns the formfactor extension for current formfactor (like .mobile or .tablet)
Example:
<?php
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
$formFactorExtension = $mainTemplate->detectFormfactorExtension();
?>
inc($file[, $additionalparams ])
Parameters
• $file (string) – the name of the template
• $additionalparams (array) – an array with additional variables which should be used for
the included template
Returns returns content of included template as a string
Includes another template. use <?php print_unescaped($this->inc(‘template’)); ?> to do this. The included
template has access to all parent template variables!
Example:
<div>
<?php print_unescaped($this->inc(’nav.inc’, array(’active’ => ’nav_entry_1’)); ?>
</div>
printPage()
Returns true when there is content to print
3.3. App Development (ownCloud App API)
43
ownCloud Developer Manual, Release 6.0
This function proceeds the template and prints its output.
Example:
<?php
$mainTemplate = new OC_Template(’news’, ’main’, ’user’);
$mainTemplate->assign(’test’, array("test", "test2"));
$mainTemplate->printPage();
?>
printAdminPage($application, $name[, $parameters ])
Parameters
• $application (string) – The application we render the template for
• $name (string) – Name of the template
• $parameters (array) – Parameters for the template
Returns bool
Shortcut to print a simple page for admin
Example:
Todo
provide example
printGuestPage($application, $name[, $parameters ])
Parameters
• $application (string) – The application we render the template for
• $name (string) – Name of the template
• $parameters (array) – Parameters for the template
Returns bool
Shortcut to print a simple page for guests
Example:
Todo
provide example
printUserPage($application, $name[, $parameters ])
Parameters
• $application (string) – The application we render the template for
• $name (string) – Name of the template
• $parameters (array) – Parameters for the template
Returns bool
Shortcut to print a simple page for users
Example:
44
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Todo
provide example
Template functions
These functions are automatically available in all templates.
html_select_options
html_select_options($options, $selected[, $params ])
Parameters
• $options (array) – an array of the form value => label
• $selected (string/array) – an array containing strings or a simple string which sets a value
as selected
• $params (array) – optional parameters that are done in key => value
Returns the html as string of preset <option> tags
Todo
Fix parameters and add example
human_file_size
human_file_size($bytes)
Parameters
• $bytes (int) – the bytes that we want to convert to a more readable format
Returns the human readable size as string
Turns bytes into human readable formats, for instance 1024 bytes get turned into 1kb, 1024*1024 bytes get turned into
1mb
<?php
// this would print <li>2kB</li>
?>
<li><?php p($this->human_file_size(’2048’)); ?></li>
image_path
image_path($app, $image)
Parameters
• $app (string) – the name of your app as a string. If the string is empty, ownCloud looks for
the image in core
• $image (array) – the filename of the image
Returns the absolute URL to the image as a string
This function looks up images in several common directories and returns the full link to it. The following directories
are being searched:
• /themes/$theme/apps/$app/img/$image
3.3. App Development (ownCloud App API)
45
ownCloud Developer Manual, Release 6.0
• /themes/$theme/$app/img/$image
• /$app/img/$image
When you pass an empty string for $app, the following directories will be searched:
• /themes/$theme/apps/$app/img/$image
• /themes/$theme/core/img/$image
• /core/img/$image
Example:
<img src="<?php print_unescaped(
image_path(’news’, ’starred.svg’);
); ?>" />
link_to
link_to($app, $file[, $args ])
Parameters
• $app (string) – the name of your app as a string. If the string is empty, ownCloud asumes
that the file is in /core/
• $file (string) – the relative path from your apps root to the file you want to access
• $args (array) – the GET parameters that you want set in the URL in form key => value.
The value will be run through urlencode()
Returns the absolute URL to the file
This function is used to produce generate clean and absolute links to your files or pages.
Example:
<?php
// this will produce the link:
// index.php/news/pages/weather.php?show=berlin
?>
<ul>
<li><a href="<?php
print_unescaped(
link_to(’news’, ’pages/weather.php’, array("show" => "berlin"));
);
?>">Show Weather for Berlin</a></li>
</ul>
mimetype_icon
mimetype_icon($mimetype)
Parameters
• $mimetype (array) – the mimetype for which we want to look up the icon
Returns the absolute URL to the icon
A shortcut for getting a mimetype icon.
Example:
46
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
<img src="<?php print_unescaped(
mimetype_icon(’application/xml’);
); ?>" />
p
p($data)
Parameters
• $data – the variable/array/object that should be printed
New in version 5.0. This is the print statement which prints out XSS escaped values. ownCloud does not allow the
direct usage of echo or print but enforces wrapper functions to prevent unwanted XSS vulnerabilities. If you want to
print unescaped data, look at print_unescaped
Example:
<?php $names = array("John", "Jakob", "Tom"); ?>
<div>
<ul>
<?php foreach($names as $name){ ?>
<li><?php p($name); ?></li>
<?php } ?>
</ul>
</div>
print_unescaped
print_unescaped($data)
Parameters
• $data – the variable/array/object that should be printed
New in version 5.0. This function does not escape the content for XSS. This would typically be used to print HTML
or JavaScript that is generated by the server and checked for XSS vulnerabilities.
Example:
<?php $html = "<div>Some HTML</div>"; ?>
<div>
<?php print_unescaped($html); ?>
</div>
relative_modified_date
relative_modified_date($timestamp)
Parameters
• $timestamp (int) – the timestamp from whom we compute the time span until now
Returns a relative date as string
Instead of displaying a date, it is often better to give a relative date like: “2 days ago” or “3 hours ago”. This function
turns a timestamp into a relative date.
<?php
// this would print <span>5 minutes ago</span>
?>
<span><?php p(relative_modified_date(’29393992912’)); ?></span>
3.3. App Development (ownCloud App API)
47
ownCloud Developer Manual, Release 6.0
simple_file_size
simple_file_size($bytes)
Parameters
• $bytes (int) – the bytes that we want to convert to a more readable format in megabytes
Returns the human readable size as string
A more simpler function that only turns bytes into megabytes. If its smaller than 0.1 megabytes, < 0.1 is being returned.
If its bigger than 1000 megabytes, > 1000 is being returned.
<?php
// this would print <li>&lt 0.1</li>
?>
<li><?php p(simple_file_size(’2048’)); ?></li>
Further reading
• http://en.wikipedia.org/wiki/Cross-site_scripting
• https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
• https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
View
class OC\Files\View
__construct($root)
Parameters
• $root (mixed) –
getAbsolutePath($path=’/’)
Parameters
• $path (mixed) –
chroot($fakeRoot)
Parameters
• $fakeRoot (string) –
Returns bool
change the root to a fake root
getRoot()
Returns string
get the fake root
getRelativePath($path)
Parameters
• $path (string) –
Returns string
48
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
get path relative to the root of the view
getMountPoint($path)
Parameters
• $path (string) –
Returns string
get the mountpoint of the storage object for a path( note: because a storage is not always mounted inside
the fakeroot, thereturned mountpoint is relative to the absolute root of the filesystemand doesn’t take the
chroot into account )
resolvePath($path)
Parameters
• $path (string) –
Returns array consisting of the storage and the internal path
resolve a path to a storage and internal path
getLocalFile($path)
Parameters
• $path (string) –
Returns string
return the path to a local version of the filewe need this because we can’t know if a file is stored local or
not fromoutside the filestorage and for some purposes a local file is needed
getLocalFolder($path)
Parameters
• $path (string) –
Returns string
mkdir($path)
Parameters
• $path (mixed) –
the following functions operate with arguments and return values identicalto those of their PHP built-in
equivalents. Mostly they are merely wrappersfor OCFilesStorageStorage via basicOperation().
rmdir($path)
Parameters
• $path (mixed) –
opendir($path)
Parameters
• $path (mixed) –
readdir($handle)
Parameters
• $handle (mixed) –
3.3. App Development (ownCloud App API)
49
ownCloud Developer Manual, Release 6.0
is_dir($path)
Parameters
• $path (mixed) –
is_file($path)
Parameters
• $path (mixed) –
stat($path)
Parameters
• $path (mixed) –
filetype($path)
Parameters
• $path (mixed) –
filesize($path)
Parameters
• $path (mixed) –
readfile($path)
Parameters
• $path (mixed) –
isCreatable($path)
Parameters
• $path (mixed) –
isReadable($path)
Parameters
• $path (mixed) –
isUpdatable($path)
Parameters
• $path (mixed) –
isDeletable($path)
Parameters
• $path (mixed) –
isSharable($path)
Parameters
• $path (mixed) –
file_exists($path)
Parameters
• $path (mixed) –
50
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
filemtime($path)
Parameters
• $path (mixed) –
touch($path, $mtime=null)
Parameters
• $path (mixed) –
• $mtime (mixed) –
file_get_contents($path)
Parameters
• $path (mixed) –
file_put_contents($path, $data)
Parameters
• $path (mixed) –
• $data (mixed) –
unlink($path)
Parameters
• $path (mixed) –
deleteAll($directory, $empty=false)
Parameters
• $directory (mixed) –
• $empty (mixed) –
rename($path1, $path2)
Parameters
• $path1 (mixed) –
• $path2 (mixed) –
copy($path1, $path2)
Parameters
• $path1 (mixed) –
• $path2 (mixed) –
fopen($path, $mode)
Parameters
• $path (mixed) –
• $mode (mixed) –
toTmpFile($path)
Parameters
• $path (mixed) –
3.3. App Development (ownCloud App API)
51
ownCloud Developer Manual, Release 6.0
fromTmpFile($tmpFile, $path)
Parameters
• $tmpFile (mixed) –
• $path (mixed) –
getMimeType($path)
Parameters
• $path (mixed) –
hash($type, $path, $raw=false)
Parameters
• $type (mixed) –
• $path (mixed) –
• $raw (mixed) –
free_space($path=’/’)
Parameters
• $path (mixed) –
hasUpdated($path, $time)
Parameters
• $path (string) –
• $time (int) –
Returns bool
check if a file or folder has been updated since $time
getFileInfo($path)
Parameters
• $path (string) –
Returns array returns an associative array with the following keys:- size- mtime- mimetypeencrypted- versioned
get the filesystem info
getDirectoryContent($directory, $mimetype_filter=’‘)
Parameters
• $directory (string) – path under datadirectory
• $mimetype_filter (mixed) –
Returns array
get the content of a directory
putFileInfo($path, $data)
Parameters
• $path (string) –
52
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• $data (array) –
Returns int returns the fileid of the updated file
change file metadata
search($query)
Parameters
• $query (string) –
Returns array
search for files with the name matching $query
searchByMime($mimetype)
Parameters
• $mimetype (mixed) –
Returns array
search for files by mimetype
getOwner($path)
Parameters
• $path (string) –
Returns string
Get the owner for a file or folder
getETag($path)
Parameters
• $path (string) –
Returns string
get the ETag for a file or folder
getPath($id)
Parameters
• $id (int) –
Returns string
Get the path of a file by id, relative to the view Note that the resulting path is not guarantied to be unique
for the id, multiple paths can point to the same file
3.4 App Development (App Framework)
3.4.1 App Tutorial
This tutorial contains the MVC approach to write an app and continues where Creating An App left off. The result will
be a simple “Hello World” app.
To make use of the App Framwork app it must be cloned and activated first by linking it into the app directory:
3.4. App Development (App Framework)
53
ownCloud Developer Manual, Release 6.0
cd /var/www
sudo git clone https://github.com/owncloud/appframework.git
sudo chown -R user:group /var/www/appframework
sudo ln -s /var/www/appframework /var/www/owncloud/apps
Note: This is only recommended for development! If a normal installation is used, place it inside the apps directory!
After that activate it on the apps page.
Create an navigation entry
The app.php will always loaded for every app and can for instance be used to load additional JavaScript for the files
app. Therefore the navigation entry has to be registered in this file.
Note: The icon img/example.png needs to exist or the navigation will throw an error
appinfo/app.php
<?php
namespace OCA\MyApp;
// dont break owncloud when the appframework is not enabled
if(\OCP\App::isEnabled(’appframework’)){
$api = new \OCA\AppFramework\Core\API(’myapp’);
$api->addNavigationEntry(array(
// the string under which your app will be referenced in owncloud
’id’ => $api->getAppName(),
// sorting weight for the navigation. The higher the number, the higher
// will it be listed in the navigation
’order’ => 10,
// the route that will be shown on startup
’href’ => $api->linkToRoute(’myapp_index’),
// the icon that will be shown in the navigation
// this file needs to exist in img/example.png
’icon’ => $api->imagePath(’example.png’),
// the title of your application. This will be used in the
// navigation or on the settings page of your app
’name’ => $api->getTrans()->t(’My notes app’)
));
} else {
$msg = ’Can not enable the MyApp app because the App Framework App is disabled’;
\OCP\Util::writeLog(’myapp’, $msg, \OCP\Util::ERROR);
}
54
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
First Page
Now that the basic files are created, the following things are needed to create a page:
• A route: The URL which links to the controller
• A controller: Gets the request and returns a response
• An entry in the DIContainer: This makes the controller available for the application
• A template: HTML which should be displayed on the page
Create the main route
Routes map the URL to functions and allow to extract values. To show the content when the navigation entry is clicked,
the index route which was defined in the appinfo/app.php needs to be created:
appinfo/routes.php
<?php
namespace OCA\MyApp;
use \OCA\AppFramework\App;
use \OCA\MyApp\DependencyInjection\DIContainer;
$this->create(’myapp_index’, ’/’)->action(
function($params){
// call the index method on the class PageController
App::main(’PageController’, ’index’, $params, new DIContainer());
}
);
Write the logic (Controller)
The Controllers to which the route links does not exist yet and it has to be created:
controller/pagecontroller.php
<?php
namespace OCA\MyApp\Controller;
use \OCA\AppFramework\Controller\Controller;
class PageController extends Controller {
public function __construct($api, $request){
parent::__construct($api, $request);
}
/**
* ATTENTION!!!
* The following comments turn off security checks
* Please look up their meaning in the documentation!
3.4. App Development (App Framework)
55
ownCloud Developer Manual, Release 6.0
*
* @CSRFExemption
* @IsAdminExemption
* @IsSubAdminExemption
*/
public function index(){
return $this->render(’main’, array(
’msg’ => ’Hello World’
));
}
}
Create the template
Now create the Templates which contains the HTML
templates/main.php
<div>{{ msg }}</div>
Wire everything together
The last thing that is left is to tell the application how the controller needs to be created. The App Framework makes
heavy use of Dependency Injection and provides an IoC Container. Inside this container, the controller needs to be
created:
dependencyinjection/dicontainer.php
<?php
namespace OCA\MyApp\DependencyInjection;
use \OCA\AppFramework\DependencyInjection\DIContainer as BaseContainer;
use \OCA\MyApp\Controller\PageController;
class DIContainer extends BaseContainer {
public function __construct(){
parent::__construct(’myapp’);
// use this to specify the template directory
$this[’TwigTemplateDirectory’] = __DIR__ . ’/../templates’;
$this[’PageController’] = function($c){
return new PageController($c[’API’], $c[’Request’]);
};
}
}
Congratulations! The message “Hello World” can now be seen on the main page of your app.
56
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
3.4.2 App Metadata
The appinfo/info.xml contains metadata about the app:
<?xml version="1.0"?>
<info>
<id>yourappname</id>
<name>Your App</name>
<description>Your App description</description>
<version>1.0</version>
<licence>AGPL</licence>
<author>Your Name</author>
<require>5</require>
<types>
<type>filesystem</type>
</types>
<remote>
<file id="caldav">appinfo/caldav.php</file>
</remote>
<documentation>
<user>http://doc.owncloud.org</user>
<admin>http://doc.owncloud.org</admin>
</documentation>
<website>http://www.owncloud.org</website>
<public>
<file id="caldav">appinfo/caldav.php</file>
</public>
<standalone />
<default_enable />
<shipped>true</shipped>
</info>
id
Required: This field contains the internal app name, and has to be the same as the foldername of the app. This id
needs to be unique in ownCloud, meaning no other app should have this id.
name
Required: This is the human-readable name/title of the app that will be displayed in the app overview page.
description
Required: This contains the description of the app which will be shown in the apps overview page.
3.4. App Development (App Framework)
57
ownCloud Developer Manual, Release 6.0
version
Contains the version of your app. Please also provide the same version in the appinfo/version.
licence
Required: The licence of the app. This licence must be compatible with the AGPL and must not be proprietary, for
instance:
• AGPL 3 (recommended)
• MIT
If a proprietary/non AGPL compatible licence should be used, the ownCloud Enterprise Edition must be used.
author
Required: The name of the app author or authors.
require
Required: The minimal version of ownCloud.
types
ownCloud allows to specify four kind of “types”. Currently supported “types”:
• prelogin: apps which needs to load on the login page
• filesystem: apps which provides filesystem functionality (e.g. files sharing app)
• authentication: apps which provided authentication backends
• logging: apps which implement a logging system
public
Used to provide a public interface (requires no login) for the app.
cloud/index.php/public. Example with id set to ‘calendar’:
The id is appended to the URL /own-
/owncloud/index.php/public/calendar
Also take a look at externalapi.
remote
Same as public but requires login. The id is appended to the URL /owncloud/index.php/remote. Example with id set
to ‘calendar’:
/owncloud/index.php/remote/calendar
Also take a look at externalapi.
58
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
documentation
link to ‘admin’ and ‘user’ documentation
website
link to project webpage
standalone
Can be set to true to indicate that this app is a webapp. This can be used to tell GNOME Web for instance to treat this
like a native application.
default_enable
Core apps only: Used to tell ownCloud to enable them after the installation.
shipped
Core apps only: Used to tell ownCloud that the app is in the standard release.
Please note that if this attribute is set to FALSE or not set at all, every time you disable the application, all the files of
the application itself will be REMOVED from the server!
3.4.3 Classloader
The classloader is provided by ownCloud and loads all your classes automatically. The only thing left to include
by yourself are 3rdparty libraries. Note that this means that the classes need to be named and organized in folders
according to their full qualifier.
The classloader works like this:
• Take the full qualifier of a class:
\OCA\AppTemplateAdvanced\Db\ItemMapper
• If it starts with \OCA include file from the apps directory
• Cut off \OCA:
\AppTemplateAdvanced\Db\ItemMapper
• Convert all charactes to lowercase:
\apptemplateadvanced\db\itemmapper
• Replace \ with /:
/apptemplateadvanced/db/itemmapper
• Append .php:
/apptemplateadvanced/db/itemmapper.php
• Prepend /apps because of the OCA namespace and include the file:
3.4. App Development (App Framework)
59
ownCloud Developer Manual, Release 6.0
require ’/apps/apptemplateadvanced/db/itemmapper.php’;
Remember:
for it to be autoloaded, the itemmapper.php needs to either be stored in the
/apps/apptemplateadvanced/db/ folder, or adjust its namespace according to the folder it’s stored in.
3.4.4 Runtime configuration
The App Framework assembles the application by using an Inversion of Control container which does Dependency
Injection. Dependency Injection helps you to create testable code. For a very simple and good Tutorial, watch the
Dependency Injection and the art of Services and Containers Tutorial on YouTube. A broader overview over how it
works and what the benefits are can be seen on Google’s Clean Code Talks.
The container is configured in dependencyinjection/dicontainer.php. By default Pimple is used as
dependency injection container. A tutorial can be found here
To add your own classes simply open the dependencyinjection/dicontainer.php and add a line like this
to the constructor:
<?php
class DIContainer extends OCA\AppFramework\DependencyInjection\DIContainer {
public function __construct(){
// tell parent container about the app name
parent::__construct(’myapp’);
$this[’MyClass’] = function($c){
return new MyClass($c[’SomeOtherClass’]);
};
}
}
You can also inject and overwrite already existing items from the App Framework.
The App Framework lets you inject/overwrite the following items:
• API: The API layer. Overwrite this if you use an API layer that inherited from the App Framework API layer
and provides additional methods.
• Request: The Request object which holds the $_POST, $_GET, etc. variables
• TwigTemplateDirectory: If set to the template directory, Twig templates can be used.
• TwigTemplateCacheDirectory: Set this to enable caching for Twig templates
• MiddlewareDispatcher: Can be used to add aditional middleware
3.4.5 API abstraction layer
ownCloud currently has a ton of static methods which is a very bad thing concerning testability. Therefore the App
Framework comes with an OCA\AppFramework\Core\API abstraction layer (basically a facade) which wraps
the static method calls inside an object.
Note: This is a temporary solution until ownCloud offers a proper API with normal classes that can be used in the
DIContainer.
This will allow you to easily mock the API in your unittests.
60
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Extend the API
If you find yourself in need to use more ownCloud internal static methods simply inherit from the API class:
core/api.php
<?php
namespace MyApp\Core;
class API extends \OCA\AppFramework\Core\API {
public function __construct($appName){
parent::__construct($appName);
}
public function methodName($someParam){
\OCP\Util::methodName($this->appName, $someParam);
}
}
and wire it up in the container:
dependencyinjection/dicontainer.php
<?php
use \OCA\MyApp\Core\API;
class DIContainer extends OCA\AppFramework\DependencyInjection\DIContainer {
public function __construct(){
// tell parent container about the app name
parent::__construct(’myapp’);
$this[’API’] = $this->share(function($c){
return new API($c[’AppName’]);
});
}
}
?>
3.4.6 Routes
PHP usually treats the URL like a filepath. This is easy for beginners but gets more complicated if a good architecture
is required. For instance if an URL should call a certain function/method or if values should be extracted from the
URL.
Routing connects your URLs with your controller methods and allows you to create constant and nice URLs. Its also
easy to extract values from the URLs.
ownCloud uses Symphony Routing
Routes are declared in appinfo/routes.php
A simple route would look like this:
3.4. App Development (App Framework)
61
ownCloud Developer Manual, Release 6.0
<?php
// this route matches /index.php/yourapp/myurl/SOMEVALUE
$this->create(’yourappname_routename’, ’/myurl/{key}’)->action(
function($params){
require __DIR__ . ’/../index.php’;
}
);
The first argument is the name of your route. This is used as an identifier to get the URL of the route and is a nice
way to generate the URL in your templates or JavaScript for certain links since it does not force you to hardcode your
URLs.
Note: The identifier should always start with the appid since they are global and you could overwrite a route of a
different app
The second parameter is the URL which should be matched. You can extract values from the URL by using {key} in
the section that you want to get. That value is then available under $params[’key’], for the above example it would
be $params[’key’]. You can omit the parameter if you dont extract any values from the URL at all.
If a default value should be used for an URL parameter, it can be set via the defaults method:
<?php
$this->create(’yourappname_routename’, ’/myurl/{key}’)->action(
function($params){
require __DIR__ . ’/../index.php’;
}
)->defaults(’key’ => ’john’);
The action method allows you to register a callback which gets called if the route is matched. You can use this to call
a controller or simply include a PHP file.
Using routes in templates and JavaScript
To use routes in OC_Template, use:
<?
print_unescaped(\OCP\Util::linkToRoute( ’yourappname_routename’, array(’key’ => 1)));
In JavaScript you can get the URL for a route like this:
var params = {key: 1};
var url = OC.Router.generate(’yourappname_routename’, params);
console.log(url); // prints /index.php//yourappname/myurl/1
Note: Be sure to only use the routes generator after the routes are loaded. This can be done by registering a callback
with OC.Router.registerLoadedCallback(callback)
Using Controllers
To call your controllers the App Framework provides a main method: OCA\AppFramework\App.
62
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Note:
If you call a controller directly no security checks will be performed! Security checks are handled by the OCA\AppFramework\Middleware\Security\SecurityMiddleware and called inside the
OCA\AppFramework\App::main method! Always use the OCA\AppFramework\App::main method!
<?php
use \OCA\AppFramework\App;
use \OCA\YourApp\DependencyInjection\DIContainer;
$this->create(’yourappname_routename’, ’/myurl/{key}’)->action(
function($params){
App::main(’MyController’, ’methodName’, $params, new DIContainer());
}
)->defaults(’key’ => ’john’);
The first parameter is the
name under
dependencyinjection/dicontainer.php.
which
the
controller
was
defined
in
the
The second parameter is the name of the method that should be called on the controller.
The third parameter is the $params array which is passed to the controller and available by using $this->params($key)
in the controller method. In the following example, the parameter in the URL would be accessible by using: $this>params(‘key’)
You can also limit the route to GET or POST requests by simply adding ->post() or ->get() before the action method
like:
<?php
use \OCA\AppFramework\App;
use \OCA\YourApp\DependencyInjection\DIContainer;
$this->create(’yourappname_routename’, ’/myurl/{key}’)->post()->action(
function($params){
App::main(’MyController’, ’methodName’, $params, new DIContainer());
}
);
?>
The fourth parameter is an instance of the DIContainer (see Dependency Injection). If you want to replace objects in
the container only for a certain request, you can do it like this:
<?php
use \OCA\AppFramework\App;
use \OCA\YourApp\DependencyInjection\DIContainer;
$this->create(’yourappname_routename’, ’/myurl/{key}’)->post()->action(
function($params){
$container = new DIContainer();
$container[’SomeClass’] = function($c){
return new SomeClass(’different’);
}
App::main(’MyController’, ’methodName’, $params, $container);
}
);
?>
Twig
The Twig templates also provide a function to create a link from a route url() function:
3.4. App Development (App Framework)
63
ownCloud Developer Manual, Release 6.0
{{ url(’yourappname_routename’, {key: ’1’}) }}
3.4.7 Controllers
The
App
Framework
provides
a
simple
baseclass
for
adding
controllers:
OCA\AppFramework\Controller\Controller. Controllers connect your view (templates) with your
database and contain the logic of your app. Controllers themselves are connected to one or more routes. Controllers
go into the controller/ directory.
A controller should be created for each resource. Think of it as an URL scheme:
/controller/method/params
For instance:
/file/1
In this case we would create a controller named FileController and the method would be called get().
A simple controller would look like:
<?php
namespace OCA\YourApp\Controller;
use \OCA\AppFramework\Controller\Controller;
use \OCA\AppFramework\Http\JSONResponse;
class MyController extends Controller {
/**
* @param Request $request an instance of the request
* @param API $api an api wrapper instance
*/
public function __construct($api, $request){
parent::__construct($api, $request);
}
/**
* @Ajax
*
* sets a global system value
*/
public function myControllerMethod(){
return new JSONResponse(array(’value’ => $this->params(’somesetting’)));
}
}
?>
An instance of the API is passed via Dependency Injection,
the same goes for a
OCA\AppFramework\Http\Request instance. URL Parameters, POST, GET and FILES parameters are
partly abstracted by the Request class and can be accessed via $this->params(‘myURLParamOrPostOrGetKey’)
and $this->getUploadedFile($key) inside the controller. This has been done to make the app better testable.
64
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
If you want to access environment variables($_ENV), use $this->env($key). Session and cookie variables can
be accessed via session and cookie methods. For example, to get a value of a variable from cookie: $this>cookie($key_name). Not like cookie, which is read only, session variables can also be changed using: $this>session($key_name, $value_name).
Every controller method has to return a Response object. The currently available Responses from the App Framework
include:
• OCA\AppFramework\Http\Response: response for sending headers only
• OCA\AppFramework\Http\JSONResponse: sends JSON to the client
• OCA\AppFramework\Http\TemplateResponse: renders a template
• OCA\AppFramework\Http\RedirectResponse: redirects to a new URL
• OCA\AppFramework\Http\TextDownloadResponse: prompts the user to download a text file containing a passed string
• OCA\AppFramework\Http\TextResponse: for printing text like XML
New in version 6.0.
• OCA\AppFramework\Http\ForbiddenResponse: returns 403 Forbidden HTTP status. If you want to
transport content, use a different response and set the HTTP status code in there
• OCA\AppFramework\Http\NotFoundResponse: returns 404 Not Found HTTP status. If you want to
transport content, use a different response and set the HTTP status code in there
Should you require to set additional headers, you can use the OCA\AppFramework\Http\Response::addHeader
method that every Response has.
Because TemplateResponse is quite common, the controller provides a shortcut method for both of them, namely
$this->render:
<?
/**
* @CSRFExemption
*/
public function index(){
$templateName = ’main’;
$params = array(
’somesetting’ => ’How long will it take’
);
return $this->render($templateName, $params);
}
For security reasons, all security checks for controller methods are turned on by default. To explicitely turn off checks,
you must use exemption annotations above the desired method.
In this example, all security checks would be disabled (not recommended):
<?php
/**
* @CSRFExemption
* @IsAdminExemption
* @IsLoggedInExemption
* @IsSubAdminExemption
*/
public function index(){
$templateName = ’main’;
3.4. App Development (App Framework)
65
ownCloud Developer Manual, Release 6.0
$params = array(
’somesetting’ => ’How long will it take’
);
return $this->render($templateName, $params);
}
Possible Annotations contain:
• @CSRFExemption: Turns off the check for the CSRF token. Only use this for the index page!
• @IsAdminExemption: Turns off the check if the user is an admin
• @IsLoggedInExemption: Turns off the check if the user is logged in
• @IsSubAdminExemption: Turns off the check if the user is a subadmin
• @Ajax: Use this for Ajax Requests. It prevents the unneeded rendering of the apps navigation and returns error
messages in JSON format
Don’t
forget
to
add
your
controller
dependencyinjection/dicontainer.php
to
the
dependency
injection
container
in
<?php
// in the constructor function
$this[’MyController’] = function($c){
return new MyController($c[’API’], $c[’Request’]);
};
?>
3.4.8 Database Schema
ownCloud uses a database abstraction layer on top of either MDB2 or PDO, depending on the availability of PDO on
the server.
The database schema is inside appinfo/database.xml in MDB2’s XML scheme notation where the placeholders
*dbprefix* (*PREFIX* in your SQL) and *dbname* can be used for the configured database table prefix and database
name.
An example database XML file would look like this:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<table>
<name>*dbprefix*yourapp_items</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
66
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
</field>
<field>
<name>user</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>name</name>
<type>text</type>
<notnull>true</notnull>
<length>100</length>
</field>
<field>
<name>path</name>
<type>clob</type>
<notnull>true</notnull>
</field>
</declaration>
</table>
</database>
To update the tables used by the app, simply adjust the database.xml file and increase the app version number in
appinfo/version to trigger an update.
3.4.9 Database Access
Your database layer should go into the db/ folder. It’s recommended to split the data entities from the database queries.
You can do that by creating a very simple PHP object which extends the class OCA\AppFramework\Db\Entity.
This object will hold the data.
Getters and setters will automatically be created for all public attributes. Should it be required to use special setter or
getters, simply create the method. Make sure to mark the field as updated by calling the parent method.
Note: Setters mark a field as updated. The OCA\AppFramework\Db\Entity::fromRow method should
only be used to pass in rows from a database result. Fields will not be marked as updated by using
OCA\AppFramework\Db\Entity::fromRow!
OCA\AppFramework\Db\Entity::fromRow maps the database columns to attributes. The conversion works
like the this:
• database column my_name gets transformed to attribute myName.
• the attribute myAwesomeProperty gets transformed to the database column my_awesome_property
Changed in version 6.0: Instead of manually creating and mapping all the entities, this is is now done by extending a
parent class. db/item.php
<?php
namespace \OCA\YourApp\Db;
use \OCA\AppFramework\Db\Entity;
class Item extends Entity {
// Note: a field id is set automatically by the parent class
public $name;
3.4. App Development (App Framework)
67
ownCloud Developer Manual, Release 6.0
public $path;
public $user;
public $timestamp;
public function __construct(){
// cast timestamp to an int when fromRow is being called
// the second parameter is the argument that is passed to
// the php function settype()
$this->addType(’timestamp’, ’int’);
}
// transform username to lower case
public function setName($name){
$name = strtolower($name);
parent::setName($name);
}
}
All database queries for that object should be put into a mapper class. This follows the data mapper pattern. Simply
inherit the OCA\AppFramework\Db\Mapper. Changed in version 6.0:: Methods from the old mapper have been
removed and deprecated to allow a small ORM. db/itemmapper.php
<?php
namespace \OCA\YourApp\Db;
use \OCA\AppFramework\Db\Mapper;
class ItemMapper extends Mapper {
public function __construct(API $api) {
parent::__construct($api, ’news_feeds’); // tablename is news_feeds
}
public function find($id, $userId){
$sql = ’SELECT * FROM ‘’ . $this->getTableName() . ’‘ ’ .
’WHERE ‘id‘ = ? ’ .
’AND ‘user_id‘ = ?’;
// use findOneQuery to throw exceptions when no entry or more than one
// entries were found
$row = $this->findOneQuery($sql, array($id, $userId));
$feed = new Item();
$feed->fromRow($row);
return $feed;
}
public function findByName($name){
$sql = ’SELECT * FROM ‘’ . $this->getTableName() . ’‘ ’ .
’WHERE ‘name‘ = ? ’;
$row = $this->execute($sql, array($name));
68
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
$feed = new Item();
$feed->fromRow($row);
return $feed;
}
}
Note: Always use ? to mark placeholders for arguments in SQL queries and pass the arguments as a second parameter
to the execute function to prevent SQL Injection
DONT:
<?php
$sql = ’SELECT * FROM ‘’ . $this->getTableName() . ’‘ WHERE ‘user‘ = ’ . $user;
$result = $this->execute($sql);
DO:
<?php
$sql = ’SELECT * FROM ‘’ . $this->getTableName() . ’‘ WHERE ‘user‘ = ?’;
$params = array($userId);
$result = $this->execute($sql, $params);
The mapper class comes with simple methods for deleting, updating and finding items. To delete a database entry,
simply pass an entity with an id to the OCA\AppFramework\Db\Mapper::delete method.
Example:
<?php
// delete the item with id 4
$item = new Item();
$item->setId(4);
$mapper = new ItemMapper($api); // inject API class for db access
$mapper->delete($item);
The same works for updating. Only the fields which have been set with setters will be updated.
Example:
<?php
// change the name of item with id 4
$item = new Item();
$item->setId(4);
$item->setName(’tony’);
$mapper = new ItemMapper($api); // inject API class for db access
$mapper->update($item);
3.4.10 Templates
ownCloud provides its own templating system.
3.4. App Development (App Framework)
69
ownCloud Developer Manual, Release 6.0
In every template file you can easily access the template functions listed in OC Templates. To access the assigned variables in the template, use the $_[] array. The variable will be availabe under the key that you defined (e.g. $_[’key’]).
templates/main.php
<?php foreach($_[’entries’] as $entry){ ?>
<p><?php p($entry); ?></p>
<?php
}
print_unescaped($this->inc(’sub.inc’));
?>
Warning: Changed in version 5.0. To prevent XSS the following PHP functions for printing are forbidden:
echo, print() and <?=. Instead use the p() function for printing your values. Should you require unescaped
printing, double check for XSS and use: print_unescaped.
Templates can also include other templates by using the $this->inc(‘templateName’) method. Use this if you find
yourself repeating a lot of the same HTML constructs.
The parent variables will also be available in the included templates, but should you require it, you can also pass new
variables to it by using the second optional parameter as array for $this->inc.
templates/sub.inc.php
<div>I am included but i can still access the parents variables!</div>
<?php p($_[’name’]); ?>
<?php print_unescaped($this->inc(’other_template’, array(’variable’ => ’value’))); ?>
For more info, see OC Templates
Templates are abstracted by the TemplateResponse object and used and returned
side the controller method.
Variables can be assigned to the Template by using
OCA\AppFramework\Http\TemplateResponse::setParams method:
inthe
controllers/yourcontroller.php
<?php
use \OCA\AppFramework\Http\TemplateResponse;
// inside the controller
public function index(){
// main is the template name. Owncloud will look for template/main.php
$response = new TemplateResponse($this->api, ’main’);
$params = array(’entries’ => array(’this’, ’is’, ’your’, ’father’, ’speaking’));
$response->setParams($params);
return $response;
}
The App Framework also provides the option of using Twig Templates which can optionally be enabled. Templates
reside in the templates/ folder.
70
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Twig Templates (recommended)
ownCloud templates do a bad job at preventing XSS. Therefore the App Framework comes with a second option: the
Twig Templating Language.
Twig Templates are enabled by using the Twig Middleware. If a Twig template directory is set in the
dependencyinjection/dicontainer.php, the middleware gets loaded automatically. If no directory is set,
theres no additional overhead.
To enable them in the dependencyinjection/dicontainer.php, add the following line to the constructor:
<?php
// in the constructor
// use this to specify the template directory
$this[’TwigTemplateDirectory’] = __DIR__ . ’/../templates’;
Twig can also cache templates as simple PHP files. To make use of this, create a cache/ directory in your app and add
the following line to the dependencyinjection/dicontainer.php:
<?php
// in the constructor
// if you want to cache the template directory, add this path
$this[’TwigTemplateCacheDirectory’] = __DIR__ . ’/../cache’;
A full reference can be found on the Twig Template Reference.
If you want to use Twig together with AngularJS the variable print characters {{}} of Angular will have to be adjusted.
You can do that by setting a different $interpolateProvider in the coffee/app.coffee config section:
app.config([’$interpolateProvider’, function($interpolateProvider) {
$interpolateProvider.startSymbol(’[[’);
$interpolateProvider.endSymbol(’]]’);
}]);
After adding the above lines, Angular will use [[]] for evaluation variables.
Additional Twig Extensions
The App Framework comes with additional template functions for Twig to better integrate with ownCloud. The
following additional functions are provided:
url(route, params=null)
Arguments
• route (string) – the name of the route
• params (string) – the params written like a JavaScript object
Prints the URL for a route.
An example would be:
{{ url(’yourapp_route_name’, {value: ’hi’}) }}
abs_url(route, params=null)
Arguments
3.4. App Development (App Framework)
71
ownCloud Developer Manual, Release 6.0
• route (string) – the name of the route
• params (string) – the params written like a JavaScript object
Same as url() but prints an absolute URL
An example would be:
{{ abs_url(’yourapp_route_name’, {value: ’hi’}) }}
trans(toTranslate, params=null)
Arguments
• toTranslate (string) – the string which should be translated
• params (string) – the params that should be replaced in the string
Enables translation in the templates
An example would be:
{{ trans(’Translate %s %s’, ’this’, ’and this’) }}
script(path, appName=null)
Arguments
• path (string) – path to the JavaScript file in the js/ folder in the app. The .js extension is
automatically added.
• appName (string) – name of the app. If no value is given, the current app will be used.
New in version 6.0. Adds a JavaScript file inside the template
An example would be:
// to include the js/public/app.js in your app use
{{ script(’public/app’) }}
style(path, appName=null)
Arguments
• path (string) – path to the CSS file in the css/ folder in the app. The .css extension is
automatically added.
• appName (string) – name of the app. If no value is given, the current app will be used.
New in version 6.0. Adds a CSS file inside the template
An example would be:
// to include the css/style.css in your app use
{{ style(’style’) }}
image_path(path, appName=null)
Arguments
• path (string) – path to an image file in the img/ folder in the app.
• appName (string) – name of the app. If no value is given, the current app will be used.
New in version 6.0. Returns the link to an image
An example would be:
72
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
// to include the img/icon.png in your app use
<img src="{{ image_path(’icon.png’) }}" />
link_to(path, appName=null)
Arguments
• path (string) – path to a file
• appName (string) – name of the app. If no value is given, the current app will be used.
New in version 6.0. Returns the link to a file
An example would be:
// to include the files/my.pdf in your app use
<a href="{{ link_to(’files/my.pdf’) }}">my pdf</a>
3.4.11 Static content
Static content consists of:
• img/: all images
• js/: all JavaScript files
• css/: all CSS files
Because ownCloud templates do not support template inheritance it is not possible to add your own
script tags. Therefore ownCloud provides the methods \OCPUtil::addScript(‘your_app_id’, ‘script’) and
\OCPUtil::addStyle(‘your_app_id’, ‘style’). The first parameter is the app’s id, the second one is the path relative to the js/ respectively the css/ folder without the file extension.
If you use Twig Templates, there is the script and style function, see Templates.
CSS and JavaScript are compressed by ownCloud so if the CSS or JavaScript do not seem to get updated, check if the
debug mode is enabled. To enable it see Enabling debug mode
3.4.12 CSS
Stylesheets have to be included in your templates, see Static content
If you have to include an image or css file in your CSS, prepend the following to your path:
• %appswebroot%: gets the absolute path to your app
• %webroot%: gets the absolute path to owncloud
For example:
.folder > .title {
background-image: url(’%webroot%/core/img/places/folder.svg’);
}
CSS for apps
ownCloud comes with special CSS rules for apps to make app development easier.
Todo
3.4. App Development (App Framework)
73
ownCloud Developer Manual, Release 6.0
document this
Formfactors
ownCloud automatically detects what kind of form factor you are using.
Currently supported are:
• mobile: works well on mobiles
• tablet: optimized for devices like iPads or Android Tablets
• standalone: mode where only the content of an App is shown. The header, footer and side navigation is not
visible. This is useful if ownCloud is embedded in other applications.
The auto detection can be overwritten by using the “formfactor” GET variable in the url:
index.php/myapp?formfactor=mobile
If you want to provide a different stylesheet or javascript file for mobile devices just suffix the formfactor in the
filename, like:
style.mobile.css
or:
script.tablet.css
3.4.13 Angular Setup
The following tools are recommended when including AngularJS into the app.
Recommended layout
If AngularJS should be used for the app, the following files layout is recommended. If CoffeeScript is used, use the
same layout only with the .coffee extension instead of the .js extension.
The main logic goes into:
• js/app/app.js: The main file where the module is being initiated
• js/app/directives/: folder for directives
• js/app/controllers/: folder for controllers
• js/app/filters/: folder for filters
• js/app/services/: folder for services
Tests go into:
• js/tests/stubs/app.js: Use this for initializing the container for tests
• js/tests/directives/: folder for directive tests
• js/tests/controllers/: folder for controller tests
• js/tests/filters/: folder for filter tests
• js/tests/services/: folder for service tests
74
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• js/tests/vendor/: folder for js libs that are only included in the tests for instance jquery
Configuration files go into:
• js/config/testacular_conf.js: Testacular (used for unittests) configuration
The compiled files go into:
• js/public/app.js: The compiled JavaScript
Additional JavaScript libs that are used in the app go into:
• js/vendor/
Build
The app will likely depend on various node.js tools for building the JavaScript/CoffeeScript. To make setup and
development easy it is recommended to provide a package.json inside the js/ directory.
This example contains additional dependencies for CoffeeScript. Adjust this to your needs.
js/package.json
{
"name": "owncloud-appframework",
"description": "ownCloud App Framework",
"version": "0.0.1",
"author": {
"name": "Bernhard Posselt",
"email": "[email protected]"
},
"private": true,
"homepage": "https://github.com/owncloud/apps/tree/appframework-js/appframework/",
"repository": {
"type": "git",
"url": "[email protected]:owncloud/apps.git"
},
"bugs": "https://github.com/owncloud/apps/issues",
"contributors": [],
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.0",
"grunt-cli": "~0.1.6",
"coffee-script": "~1.4.0",
"grunt-contrib-concat": "~0.1.2",
"grunt-contrib-watch": "~0.2.0",
"grunt-coffeelint": "0.0.6",
"grunt-wrap": "~0.2.0",
"phantomjs": "~1.8.1-3",
"grunt-phpunit": "0.2.0",
"gruntacular": "~0.3.0"
},
"engine": "node >= 0.8"
}
To build the JavaScript/CoffeeScript a buildsystem like Grunt is recommended. To get a good overview watch the
Tutorial video with Ben Alman.
The configfile for Grunt should be placed in the js/ directory and can either contain CoffeeScript or JavaScript.
An example for a CoffeeScript configuration would be:
3.4. App Development (App Framework)
75
ownCloud Developer Manual, Release 6.0
js/Gruntfile.coffee
module.exports = (grunt) ->
grunt.loadNpmTasks(’grunt-contrib-concat’)
grunt.loadNpmTasks(’grunt-contrib-watch’)
grunt.loadNpmTasks(’grunt-coffeelint’)
grunt.loadNpmTasks(’grunt-wrap’);
grunt.loadNpmTasks(’grunt-phpunit’);
grunt.loadNpmTasks(’gruntacular’);
grunt.initConfig
meta:
pkg: grunt.file.readJSON(’package.json’)
version: ’<%= meta.pkg.version %>’
banner: ’/**\n’ +
’ * <%= meta.pkg.description %> - v<%= meta.version %>\n’ +
’ *\n’ +
’ * Copyright (c) <%= grunt.template.today("yyyy") %> - ’ +
’<%= meta.pkg.author.name %> <<%= meta.pkg.author.email %>>\n’ +
’ *\n’ +
’ * This file is licensed under the Affero General Public License version 3 or later.\n’ +
’ * See the COPYING-README file\n’ +
’ *\n’ +
’ */\n\n’
build: ’build/’
production: ’public/’
concat:
app:
options:
banner: ’<%= meta.banner %>\n’
stripBanners:
options: ’block’
src: [
’<%= meta.build %>app/app.js’
’<%= meta.build %>app/directives/*.js’
’<%= meta.build %>app/services/**/*.js’
]
dest: ’<%= meta.production %>app.js’
wrap:
app:
src: ’<%= meta.production %>app.js’
dest: ’’
# adjust this to include more top level js libs
wrapper: [
’(function(angular, $, undefined){\n\n’
’\n})(window.angular, jQuery);’
]
coffeelint:
app: [
’app/**/*.coffee’
’tests/**/*.coffee’
]
options:
’no_tabs’:
’level’: ’ignore’
76
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
’indentation’:
’level’: ’ignore’
’no_trailing_whitespace’:
’level’: ’warn’
watch:
concat:
files:
’<%=
’<%=
]
tasks:
phpunit:
files:
tasks:
[
meta.build %>app/**/*.js’
meta.build %>tests/**/*.js’
’compile’
’../**/*.php’
[’phpunit’]
testacular:
unit:
configFile: ’config/testacular.conf.js’
continuous:
configFile: ’config/testacular.conf.js’
singleRun: true
browsers: [’PhantomJS’]
reporters: [’progress’, ’junit’]
junitReporter:
outputFile: ’test-results.xml’
phpunit:
classes:
dir: ’../tests’
options:
colors: true
grunt.registerTask(’run’, [’watch:concat’])
grunt.registerTask(’compile’, [’concat’, ’wrap’, ’coffeelint’])
grunt.registerTask(’ci’, [’testacular:continuous’])
grunt.registerTask(’testphp’, [’watch:phpunit’])
If no CoffeeScript is being used, coffeelint should be replaced with jshint and jslint.
To give people a well known environment a Makefile is recommended to start the various tasks:
firefox_bin=/usr/bin/firefox
chrome_bin=/usr/bin/chromium
coffee=$(CURDIR)/node_modules/coffee-script/bin/coffee
grunt=$(CURDIR)/node_modules/grunt-cli/bin/grunt
phantomjs=$(CURDIR)/node_modules/phantomjs/bin/phantomjs
all: compile
deps:
cd $(CURDIR)/
npm install --deps
watch: compile
$(coffee) --compile --watch --output $(CURDIR)/build/app $(CURDIR)/app/ & \
$(coffee) --compile --watch --output $(CURDIR)/build/tests $(CURDIR)/tests/ & \
$(grunt) --config $(CURDIR)/Gruntfile.coffee run
3.4. App Development (App Framework)
77
ownCloud Developer Manual, Release 6.0
testacular: deps
export CHROME_BIN=$(chrome_bin) && export FIREFOX_BIN=$(firefox_bin) && \
$(grunt) --config $(CURDIR)/Gruntfile.coffee testacular:unit
phpunit: deps
$(grunt) --config $(CURDIR)/Gruntfile.coffee testphp
compile: deps
mkdir -p $(CURDIR)/build/app
mkdir -p $(CURDIR)/build/tests
mkdir -p $(CURDIR)/public
$(coffee) --compile --output $(CURDIR)/build/app $(CURDIR)/app/
$(coffee) --compile --output $(CURDIR)/build/tests $(CURDIR)/tests/
$(grunt) --config $(CURDIR)/Gruntfile.coffee compile
test: deps compile
export PHANTOMJS_BIN=$(phantomjs) && \
$(grunt) --config $(CURDIR)/Gruntfile.coffee ci
clean:
rm -rf $(CURDIR)/build
rm -rf $(CURDIR)/test-results.xml
The above makefile can be used to watch and compile the changes with:
make watch
The unittests can be automatically run on change in a second terminal window:
make testacular
Set up Testacular
Testacular is able to run unittests when a JavaScript file changes. On the continues integration server these tests can
be run with PhantomJS (or if a graphical environment is installed also with other browsers). A JUnit compatible
testresult can be configured.
Note: The config values can be overwritten in the Gruntfile
An example file would look like:
js/config/testacular_conf.js
// base path, that will be used to resolve files and exclude
// since this is in the config/ folder we have to go one directory higher
basePath = ’../’;
// list of files / patterns to load in the browser
files = [
// your favorite test library, needs to have an adapter
JASMINE,
JASMINE_ADAPTER,
78
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
// commonly included libraries that are provided by owncloud need to be
// loaded because we dont have access to those in the test environment
’tests/vendor/jquery-1.9.1/jquery-1.9.1.js’,
’tests/vendor/jquery-ui-1.10.0/jquery-ui-1.10.0.custom.js’,
’tests/vendor/angular-1.0.4/angular.js’,
’tests/vendor/angular-1.0.4/angular-mocks.js’,
// you want to use the ngMocks container thats why you have to redefine the
// main js file
’tests/stubs/app.js’,
// these are your js and testfiles that you want to use
’build/app/directives/*.js’,
’build/app/filters/*.js’,
’build/app/services/**/*.js’,
’build/tests/**/*Spec.js’
];
// list of files to exclude
// reason: see the files array
exclude = [
’build/app/app.js’
];
// test results reporter to use
// possible values: ’dots’, ’progress’, ’junit’
reporters = [’progress’];
// web server port
port = 8080;
// cli runner port
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_INFO;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = true;
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers = [’Chrome’];
3.4. App Development (App Framework)
79
ownCloud Developer Manual, Release 6.0
// If browser does not capture in given timeout [ms], kill it
captureTimeout = 5000;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;
3.4.14 Angular App Framework Libraries
New in version 6.0. The App Framework comes with additional libraries which help to interact with the ownCloud
server.
Include script files
To make use of the the tools include them in your templates.
Using ownCloud Templates:
templates/main.php
<?php \OCP\Util::addScript(’appframework’, ’public/app’); ?>
Using Twig Templates:
templates/main.php
{{ script(’public/app’, ’appframework’) }}
After the script has been included the modules can be used inside the angular module by injecting them:
js/app/app.coffee
# create your application and inject OC
angular.module(’YourApp’, [’OC’])
General
Now that the library is loaded and set up they can be used inside the module container. The App Framework follows
the convention of marking class names with a leading underscore and object instances without it.
Note: The App Framework uses CoffeeScript so if JavaScript objects should extend the provided objects, you need
to implement the inheritance like CoffeeScript does it.
Services
The App Framework provides the following services:
Router
The OC.Router object
80
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Notification
The OC.Notification object
Utils
The OC object
_Loading
Simple object that can be used for displaying loading notifcations. It has an internal counter that starts at 0 and can be
increased and decreased. If the counter is bigger than 0 isLoading is true.
Example:
loading = new Loading()
loading.isLoading() # false
loading.increase()
loading.isLoading() # true
loading.increase()
loading.getCount() # 2
loading.decrease()
loading.decrease()
loading.isLoading() # false
_Request
Used to perform AJAX requests.
Example:
// simple GET request
req = new _Request($http, new _Publisher(), Router)
req.get(’mail_index’)
class _Request($http http, _Publisher publisher, Router router)
request(route[, data ])
Arguments
• route (object) – The name of the route that should be used
• data (object) – an object containing optional parameters
Creates an AJAX request. The following data attributes can be set:
•routeParams: object with parameters for the route
•data: ajax data objec which is passed to PHP
•onSuccess: callback for successful requests
•onFailure: callback for failed requests
3.4. App Development (App Framework)
81
ownCloud Developer Manual, Release 6.0
•config: a config which should be passed to $http
get(route[, data ])
Arguments
• route (object) – The name of the route that should be used
• data (object) – an object containing optional parameters
Shortcut for doing a GET request, for data attributes see _Request.request()
post(route[, data ])
Arguments
• route (object) – The name of the route that should be used
• data (object) – an object containing optional parameters
Shortcut for doing a POST request, for data attributes see _Request.request()
put(route[, data ])
Arguments
• route (object) – The name of the route that should be used
• data (object) – an object containing optional parameters
Shortcut for doing a PUT request, for data attributes see _Request.request()
delete(route[, data ])
Arguments
• route (object) – The name of the route that should be used
• data (object) – an object containing optional parameters
Shortcut for doing a DELETE request, for data attributes see _Request.request()
_Publisher
Used to automatically distribute JSON from AJAX Requests to the models. This is especially effective when you need
to query for data and dont want to provide a callback to pass the return value to your models.
Example: Passing folders from the server to the client’s FolderModel.
<?php
/**
* @Ajax
*/
public function getAllFolders(){
// the keys on the first level can be used
return $this->renderJSON(array(
’foldersKey’ => array(
array(’id’ => 1, ’name’ => ’Books’),
array(’id’ => 2, ’name’ => ’Stuff’)
)
));
}
They key foldersKey can now be registered on the client side by subscribing to it with:
82
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
angular.module(’YourApp’).factory ’Publisher’,
[’_Publisher’, ’FolderModel’, (_Publisher, FolderModel) ->
publisher = new _Publisher()
publisher.subscribeObjectTo(FolderModel, ’foldersKey’)
return publisher
]
Now everytime you call the getAllFolders controller method the returned JSON will be passed directly to the FolderModel.
Internally it works like this:
• For each successful request the data JSON array is iterated over
• If a key is found that a model subscribed to the data will be passed to its handle() method
The default handle() method of the model only adds/updates the new object. To add custom behaviour you can
overwrite the method.
_Model
Used as a model parent class and provides CRUD and caching logic for the JSON data.
class _Model()
add(data)
Arguments
• data (object) – The object that should be added
Adds a new item. If the item id is already present, it will be updated
update(data[, clearCache=true ])
Arguments
• data (object) – The object that should be updated
• clearCache (boolean) – clears the existing queries cache. Set this to false if the update
does not affect the queries on the model to improve performance
Updates an existing object by copying the provided attributes to the old one. That means that if you want
to update only one field simply pass an object with the correct id and the fields that should be overwritten.
Example:
// udpate name and email
itemModel.update({id: 3, name: ’newName’, email: ’newEmail’})
add(data[, clearCache=true ])
Arguments
• data (object) – The object that should be added
• clearCache (boolean) – clears the existing queries cache. Set this to false if the update
does not affect the queries on the model to improve performance
getById(id[, clearCache=true ])
3.4. App Development (App Framework)
83
ownCloud Developer Manual, Release 6.0
Arguments
• id (int) – The id of the object
• clearCache (boolean) – clears the existing queries cache. Set this to false if the update
does not affect the queries on the model to improve performance
Returns object a data object by its id
getAll()
Returns array an array with all stored objects
clear()
Deletes all stored data objects
size()
Returns int the count of all stored data objects
get(Query query)
Arguments
• query (Query) – an instance of a Query class or subclass
Returns mixed the returnvalue of the query
Runs a query over all stored objects and returns the result which is calculated in the query. This is cached
by params and query. The cache is deleted after a new add/update/remove method was called.
Queries Because AngularJS getters have to be fast (Angular checks for changed objects after each digest) the App
Framework provides cachable queries. The following queries are available:
• _BiggerThanQuery
• _BiggerThanEqualQuery
• _LessThanQuery
• _LessThanEqualQuery
• _EuqalQuery
• _NotEuqalQuery
• _ContainsQuery
• _DoesNotContainQuery
• _MinimumQuery
• _MaximumQuery
To query an object with a _BiggerThanQuery use its get method:
valuesBiggerThan4 = myModel.get(new _BiggerThanQuery(’id’, 4))
This query is cached until a new entry is added, removed or updated.
Note: Do not update the objects by hand only. Always use the model’s update method to tell it that a model has
changed. Otherwise you run into an invalid cache!
84
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Writing your own queries For more complex queries the _Query object can be extended. Each query object has a
hashCode and exec method. The hashCode method is used to generate a unique hash for the query and its arguments
so that it can be cached. The built in method works like this:
• Take all the arguments values
• replace _ with __ in the argument values
• Construct the hash by: QUERYNAME_ARG1Value_ARG2Value etc.
You can override this method if you need to. The exec method is used to run the query. It receives an array with all
objects and returns the filtered content.
A query that would select only ids between a range of numbers would look like this:
angular.module(’YourApp’).factory ’_LessThanQuery’, [’_Query’, (_Query) ->
class RangeQuery extends _Query
# @_field is the attribute name of the object
constructor: (@_field, @_lowerBound, @_upperBound) ->
name = ’range’
super(name, [@_field, @_lowerBound, @_upperBound])
exec: (data) ->
filtered = []
for entry in data
if entry[@_field] < @_upperBound and entry[@_field] > @_lowerBound
filtered.push(entry)
return filtered
return RangeQuery
]
If hashCode is not overwritten it would produce the following output:
query = new _RangeQuery(’id’, 3, 6)
query.hashCode() # prints range_id_3_6
Directives
The App Framework provides the following directives:
ocClickSlideToggle
Can be used for the settings slideup or to slide up any area and hide it on focus lost.
Can be enhanced by passing an expression:
{
selector: ’#jquery .selector’
hideOnFocusLost: true
cssClass: ’opened’
}
• selector: if defined, a different area is slid up on click
3.4. App Development (App Framework)
85
ownCloud Developer Manual, Release 6.0
• hideOnFocusLost: if defined, the slid up area will hide when the focus is lost
• cssClass: if defined, the class which should be toggled on the element where the directive is bound to
Example:
<button oc-click-slide-toggle="{selector: ’#settings’, hideOnFocusLost: true}" />
<div id="settings"></div>
ocClickFocus
Can be used to focus a different element when the element is being clicked that has this directive
Must pass an expression:
{
selector: ’#jquery .selector’,
timeout: 300
}
• selector: the area that should be focused
• timeout: optional, if the focus should be done after a timeout
Example:
<button oc-click-focus="{selector: ’#settings’}" />
<input id="settings" type="text" />
ocReadFile
Can be used to pass the contents of a file input field to a function. The directive binds to the change event of the input.
The read content will be assigned to the scope as $fileContent variable and the given function will be called.
Example:
<input type="file" name="import" oc-read-file="import($fileContent)"/>
ocDraggable
Shortcut for using jquery-ui draggable. The expression is passed to $.draggable.
These two are equivalent:
$(’#settings’).draggable({ cursor: "move", cursorAt: { top: 56, left: 56 } });
<div id="settings" oc-draggable="{ cursor: ’move’, cursorAt: { top: 56, left: 56 } }"></div>
ocForwardClick
Used to forward a click. Useful to trigger a hidden file upload field by clicking a visible button.
Needs an expression:
86
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
{
selector: ’#jquery .selector’
}
• selector: the are where the click needs to redirected to
Example:
<button oc-forward-click="{selector: ’#upload’}" />
<input type="file" id="upload" />
ocTooltip
Binds a bootstrap tooltip on the item.
Example:
<button oc-tooltip title="tooltip text" />
Filters
The App Framework provides the following filters:
ocRemoveTags
Used to remove certain HTML tags from a string.
You can pass in a string or an array of strings which HTML tags which will be removed.
Example:
<!-- remove all em tags -->
<h1>{{ title|ocRemoveTags:’em’ }}</h1>
<!-- remove all em,b and i tags -->
<h1>{{ title|ocRemoveTags:[’em’, ’i’, ’b’] }}</h1>
ocSanitizeURL
Used to sanitize an URL to prevent XSS in src and href attributes (e.g. <a href=”javascript:alert(1)”>).
Example:
<a href="{{ link|ocSanitizeURL }}">My link</a>
3.4.15 Acceptance Tests
https://github.com/cucumber/cucumber/wiki/Given-When-Then
http://www.elabs.se/blog/15you-re-cuking-it-wrong
https://github.com/cucumber/cucumber/wiki/Cucumber-Backgrounder
https://github.com/jnicklas/capybara#capybara .. 6.0 replace:: 6.0
3.4. App Development (App Framework)
87
ownCloud Developer Manual, Release 6.0
3.4.16 Unittests
The App Framework ships with several useful tools to do unittesting
PHP
Note:
App Unittests should not depend on a running ownCloud instance! They should be able to run
in isolation. To achieve that, abstract the ownCloud core functions and static methods in the App Framework core/api.php and use a mock for testing. If a class is not static, you can simply add it in the
dependencyinjection/dicontainer.php
Note: Also use your app’s namespace in your test classes to avoid possible conflicts when the test is run on the
buildserver
Unittests go into your tests/ directory. Create the same folder structure in the tests directory like on your app to make
it easier to find tests for certain classes.
ownCloud uses PHPUnit
Because of Dependency Injection, unittesting has become very easy: you can easily substitute complex classes with
mocks by simply passing a different object to the constructor.
Also using a container like Pimple frees us from doing complex instantiation and object passing in our application by
hand.
A simple test for a controller would look like this:
tests/controllers/ItemControllerTest.php
<?php
namespace OCA\YourApp;
use OCA\AppFramework\Http\Request;
use OCA\AppFramework\Db\DoesNotExistException;
use OCA\AppFramework\Utility\ControllerTestUtility;
require_once(__DIR__ . "/../classloader.php");
class ItemControllerTest extends ControllerTestUtility {
public function testSetSystemValue(){
$post = array(’somesetting’ => ’this is a test’);
$request = new Request(array(), $post);
// create an api mock object
$api = $this->getAPIMock();
// expects to be called once with the method
// setAppValue(’somesetting’, ’this is a test’)
$api->expects($this->once())
->method(’setAppValue’)
->with( $this->equalTo(’somesetting’),
$this->equalTo(’this is a test’));
88
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
// we want to return the appname yourapp when this method
// is being called
$api->expects($this->any())
->method(’getAppName’)
->will($this->returnValue(’yourapp’));
$controller = new ItemController($api, $request, null);
$response = $controller->setAppValue(null);
// check if the correct parameters of the json response are set
$this->assertEquals($post, $response->getParams());
}
}
You can now execute the test by running this in your app directory:
phpunit tests/
Note: PHPUnit executes all PHP Files that end with Test.php. Be sure to consider that in your file naming.
New in version 6.0. TDD can also be used if the Angular Setup is performed and grunt is used. To automatically run
all PHP unittests on change simply use:
cd js/
make phpunit
Classloader
The generated app has an extra classloader tests/classloader.php that loads the the classes. Require this file
at the top of your tests.
Note: The classloader in the tests/ directory assumes that the appframework/ folder is in the same directory as the
your app. If you run your app in a different apps folder, you will need to link the App Framework into the same folder
where your app folder resides.
JavaScript
New in version 6.0. If the Angular Setup was performed Testacular was already successfully set up and can be started
with:
cd js/
make testacular
Testacular now watches for changes and executes all tests if a JavaScript file is changed.
To run the tests once use:
cd js/
make test
A JUnit compatible result file will be generated for the continous integration server.
3.4. App Development (App Framework)
89
ownCloud Developer Manual, Release 6.0
Like stated in Angular Setup tests go into the folder js/tests/. The default setup uses Jasmine but also other test
frameworks like Mocha or QUnit can be used but have to be configured first.
AngularJS
To include mocks the main container creation has to be overwritten with another file.
js/tests/stubs/app.js
angular.module(’YourApp’, [’ngMock’]);
This file should be included in the testfiles in js/config/testacular_conf.js instead of js/app/app.js
Create a testfile for each JavaScript/CoffeeScript file in the tests/ folder.
Example:
describe ’_Request’, ->
# tell inject to ready the app container
beforeEach module ’YourApp’
# get _Request and _Publisher from the container
beforeEach inject (_Request, _Publisher) =>
@router =
generate: (route, values) ->
return ’url’
registerLoadedCallback: (callback) ->
callback()
@publisher = new _Publisher()
@request = _Request
it ’should not send requests if not initialized’, =>
# create a mock
http = jasmine.createSpy(’http’)
@router.registerLoadedCallback = ->
req = new @request(http, @publisher, @router)
req.request(’route’)
expect(http).not.toHaveBeenCalled()
3.4.17 Middleware
Middleware is logic that is run before and after each request and is modelled after Django’s Middleware system. It
offers the following hooks:
• beforeController: This is executed before a controller method is being executed. This allows you to plug
additional checks or logic before that method, like for instance security checks
• afterException: This is being run when either the beforeController method or the controller method itself
is throwing an exception. The middleware is asked in reverse order to handle the exception and to return a
response. If the middleware can’t handle the exception, it throws the exception again
• afterController: This is being run after a successful controllermethod call and allows the manipulation of a
Response object. The middleware is run in reverse order
90
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• beforeOutput: This is being run after the response object has been rendered and allows the manipulation of the
outputted text. The middleware is run in reverse order
To
generate
your
own
middleware,
simply
inherit
from
the
Middleware
OCA\AppFramework\Middleware\Middleware: and overwrite the methods that you want to use.
class
<?php
use \OCA\AppFramework\Middleware\Middleware;
class CensorMiddleware extends Middleware {
private $api;
/**
* @param API $api an instance of the api
*/
public function __construct($api){
$this->api = $api;
}
/**
* this replaces "fuck" with "****"" in the output
*/
public function beforeOutput($controller, $methodName, $output){
return str_replace($output, ’fuck’, ’****’);
}
}
To activate the middleware, you have to overwrite the OCA\AppFramework\Middleware\MiddlewareDispatcher:
in the DIContainer constructor:
Note: If you ship your own middleware, be sure to also enable the existing ones like the SecurityMiddleware if
you overwrite the MiddlewareDispatcher in the Dependency Injection Container! If this is forgotten, there will be
security issues!
<?php
// in the constructor
$this[’CensorMiddleware’] = function($c){
return new CensorMiddleware($c[’API’]);
};
$this[’MiddlewareDispatcher’] = function($c){
$dispatcher = new \OCA\AppFramework\Middleware\MiddlewareDispatcher();
$dispatcher->registerMiddleware($c[’HttpMiddleware’]);
$dispatcher->registerMiddleware($c[’SecurityMiddleware’]);
$dispatcher->registerMiddleware($c[’CensorMiddleware’]);
return $dispatcher;
};
Note: The order is important! The middleware that is registered first gets run first in the beforeController method.
For all other hooks, the order is being reversed, meaning: if a middleware is registered first, it gets run last.
3.4. App Development (App Framework)
91
ownCloud Developer Manual, Release 6.0
3.4.18 Filesystem
ownCloud handling of filesystems is very flexible. A variety of local and remote filesystem types are supported, as
well as a variety of hooks and optional features such as encryption and version control. It is important that apps use
the correct methods for interacting with files in order to maintain this flexibility.
In some cases using PHP’s internal filesystem functions directly will be sufficient, such as unlink() and mkdir(). Most
of the time however it is necessary to use one of ownCloud’s filesystem classes. This documentation assumes that you
are working with files stored within a user’s directory (as opposed to ownCloud core files), and therefore need to use
OC\Files\View.
Todo
write the rest
3.4.19 Hooks
In ownCloud apps, function or methods (event handlers) which are used by the app and called by ownCloud core
hooks, are generally stored in apps/appname/lib/hooks.php. Hooks are a way of implementing the observer
pattern, and are commonly used by web platform applications to provide clean interfaces to third party applications
which need to modify core application functionality.
In ownCloud, a hook is a function whose name can be used by developers of plug-ins to ensure that additional code
is executed at a precise place during the execution of other parts of ownCloud code. For example, when an ownCloud
user is deleted, the ownCloud core hook post_deleteUser is executed. In the calendar app’s appinfo/app.php,
this hook is connected to the app’s own event handler deleteUser (user here refers to an ownCloud user; deleteUser
deletes all addressbooks for that a given ownCloud user).
When post_deleteUser calls the calender app’s deleteUser event handler, it supplies it with an argument, which is an
array containing the user ID of the user that has just been deleted. This user ID is then used by the event handler to
specify which address books to delete. There are three components to the use of hooks in this example:
1. The ownCloud core hook post_deleteUser, (see what arguments / data it will provide in lib/user.php,
where it is defined)
2. The event handler deleteUser, defined in apps/contacts/lib/hooks.php.
3. The connection of the hook to the event handler, in apps/contacts/appinfo/app.php.
Hook Terminology
• Signal class / emitter class: the class that contains the method which contains the creation of the hook (and a
call to the emit() method) e.g. OC_User
• Signal / signal name: the name of the hook, e.g. post_deleteUser
• Slot class: class housing the event handling method, e.g. OC_Contacts_Hooks
• Slot name: event handler method, e.g. deleteUser (function that deletes all contact address books for a user)
92
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Available hooks
File: apps/calendar/ajax/events.php, Class: OC_Calendar
• Hook: getEvents
File: apps/calendar/index.php, Class: OC_Calendar
• Hook: getSources
File: dav.php, Class: OC_DAV
• Hook: initialize
File: lib/migrate.php, Class: OC_User
• Hook: pre_createUser
• Hook: post_createUser
File: lib/filecache.php, Class: OC_Filesystem
• Hook: post_write
• Hook: post_write
• Hook: post_delete
• Hook: post_write
File: lib/user.php, Class: OC_User
• Hook: pre_createUser
• Hook: post_createUser
• Hook: pre_deleteUser
• Hook: post_deleteUser
• Hook: pre_login
• Hook: post_login
• Hook: logout
• Hook: pre_setPassword
• Hook: post_setPassword
File: lib/group.php, Class: OC_Group
• Hook: pre_createGroup
• Hook: post_createGroup
• Hook: pre_deleteGroup
• Hook: post_deleteGroup
• Hook: pre_addToGroup
• Hook: post_addToGroup
• Hook: pre_removeFromGroup
• Hook: post_removeFromGroup
3.4. App Development (App Framework)
93
ownCloud Developer Manual, Release 6.0
3.4.20 Data Migration
As of OC4, user migration is supported. To include migration support in your app (which is highly recommended and
does not take long) you must provide a appinfo/migrate.php. The function of the migrate.php file is to provide
an import and export functions for app data. To assist in this, we set the user id of the user being exported / user being
imported in $this->uid. There is also an instance of the OC_Migration_Content class stored in $this->content. The
OC_Migration_Content class helps to make importing and exporting data easy for app developers.
Export
In this function, you must do everything necessary to export a user from the current ownCloud instance, given a user
id. For most apps this is just the case of saving a few rows from the database.
Database Data
To make exporting database data really easy, the class OC_Migration_Content has a method called copyRows()
which will save these rows for you given some options. Take a look at the export function for the bookmarks app:
<?php
function export( ){
OC_Log::write(’migration’,’starting export for bookmarks’,OC_Log::INFO);
// migrate two tables
$bookmarkOptions = array(
’table’=>’bookmarks’,
’matchcol’=>’user_id’,
’matchval’=>$this->uid,
’idcol’=>’id’
);
$bookmarkIds = $this->content->copyRows( $bookmarkOptions );
$bookmarkTagsOptions = array(
’table’=>’bookmarks_tags’,
’matchcol’=>’bookmark_id’,
’matchval’=>$ids
);
$bookmarkTagsIds = $this->content->copyRows( $bookmarkTagsOptions );
// If both returned some ids then they worked
if( is_array( $bookmarkIds ) && is_array( $bookmarkTagsIds ) )
{
return true;
} else {
return false;
}
}
The bookmarks app stores all of its data in the database, in two tables: *PREFIX*bookmarks and *PREFIX*bookmarks_tags so to export this, we need to run copyRows() twice. Here is an explanation of the options
passed to it:
• table: string name of the table to export (without any prefix)
• matchcol: (optional) string name of the column that will be matched with the value in matchval (Basically the
column used in the WHERE sql query)
94
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• matchval: (optional) the value that will be searched for in the table
• idcol: the name of the column that will be returned
To export the bookmarks, matchcol is set to the user_id column and matchval is set to the user being exported: $this>content->uid. idcol is set to the id of the bookmark, as we need to retrive the tags associated with the bookmarks for
the user being exported. The function will return an array of ids.Next we run the copyRows() method again, this time
on the bookmarks_tags table, matching a range of values (as we want to find all tags, related to all bookmarks owned
by the exported user).Finally we check that both functions returned arrays which confirms that they were successful
and return a boolean value to represent the success of the export.
Files
If you use files to hold some app data in data/userid/appname/, they will be automatically copied exported for you.
Import
Import is a little more tricky as we have to take into account data from different versions of your app, and also handle
changing primary keys. Here is the import function for the bookmarks app which imports bookmarks and tags:
<?php
function import(){
switch( $this->appinfo->version ){
default:
// All versions of the app have had the same db structure
// so all can use the same import function
$query = $this->content->prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" );
$results = $query->execute( array( $this->olduid ));
$idmap = array();
while( $row = $results->fetchRow() ){
// Import each bookmark, saving its id into the map
$sql = "INSERT INTO *PREFIX*bookmarks" .
"(url, title, user_id, public, added, lastmodified)" .
" VALUES (?, ?, ?, ?, ?, ?)";
$query = OC_DB::prepare($sql);
$query->execute( array(
$row[’url’],
$row[’title’],
$this->uid,
$row[’public’],
$row[’added’],
$row[’lastmodified’]
) );
// Map the id
$idmap[$row[’id’]] = OC_DB::insertid();
}
// Now tags
foreach($idmap as $oldid => $newid){
$sql = "SELECT * FROM bookmarks_tags WHERE user_id LIKE ?";
$query = $this->content->prepare($sql);
$results = $query->execute( array( $oldid ) );
while( $row = $data->fetchRow() ){
// Import the tags for this bookmark, using the new bookmark id
$sql = "INSERT INTO *PREFIX*bookmarks_tags(bookmark_id, tag)".
" VALUES (?, ?)";
$query = OC_DB::prepare($sql);
3.4. App Development (App Framework)
95
ownCloud Developer Manual, Release 6.0
$query->execute( array( $newid, $row[’tag’] ) );
}
}
// All done!
break;
}
return true;
}
We start off by using a switch to run different import code for different versions of your app. $this->appinfo->version
contains the version string from the appinfo/info.xml of your app. In the case of the bookmarks app the db
structure has not changed, so only one version of import code is needed.
To import the db data, first we must retrive it from the migration.db. To do this we use the prepare method from
OC_Migration_Content, which returns a MDB2 db object. We then cycle through the bookmarks in migration.db
and insert them into the owncloud database. The important bit is the idmapping. After inserting a boookmark, The
new id of the bookmark is saved in an array, with the key being the old id of the bookmark. This means when inserting
the tags, we know what the new id of the bookmark is simply by getting the value of $idmap[’oldid’].
Remember this part of the import code may be a good place to emit some hooks depending on your app. For example
the contacts app could emit some hooks to show some contacts have been added.
After importing the bookmarks, we must import the tags. It is a very similar process to importing the bookmarks,
except we have to take into account the changes in primary keys. This is done by using a foreach key in the $idmap
array, and then inserting the tags using the new id.
After all this, we must return a boolean value to indicate the success or failure of the import. Again, app data files
stored in data/userid/appname/ will be automatically copied over before the apps import function is executed, this
allows you to manipulate the imported files if necessary.
Conclusion
To fully support user migration for your app you must provide an import and export function under an instance of
OC_Migration_Provider and put this code in the file appinfo/migrate.php
3.4.21 AppFramework API
App
Entry point for every request in your app. You can consider this as your public static void main() method
Handles all the dependency injection, controllers and output flow
class OCA\AppFramework\App
static App::main($controllerName, $methodName, $urlParams, $container)
Parameters
• $controllerName (string) – the name of the controller under which it is stored in the DI
container
• $methodName (string) – the method that you want to call
• $urlParams (array) – an array with variables extracted from the routes
• $container (\Pimple) – an instance of a pimple container.
96
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Shortcut for calling a controller method and printing the result
Controller
Baseclass to inherit your controllers from
class OCA\AppFramework\Controller\Controller
•Abstract
property $api
•Protected
None
property $request
•Protected
__construct($api, $request)
Parameters
• $api (\OCA\AppFramework\Core\API) – an api wrapper instance
• $request (\OCA\AppFramework\Http\Request) – an instance of the request
params($key, $default=null)
Parameters
• $key (string) – the key which you want to access in the URL Parameter placeholder,
$_POST or $_GET array. The priority how they’re returned is the following: 1. URL
parameters 2. POST parameters 3. GET parameters
• $default (mixed) – If the key is not found, this value will be returned
Returns mixed the content of the array
Lets you access post and get parameters by the index
getParams()
Returns array the array with all parameters
Returns all params that were received, be it from the request(as GET or POST) or throuh the URL by the
route
method()
Returns string the method of the request (POST, GET, etc)
Returns the method of the request
getUploadedFile($key)
Parameters
• $key (string) – the key that will be taken from the $_FILES array
Returns array the file in the $_FILES element
Shortcut for accessing an uploaded file through the $_FILES array
env($key)
Parameters
3.4. App Development (App Framework)
97
ownCloud Developer Manual, Release 6.0
• $key (string) – the key that will be taken from the $_ENV array
Returns array the value in the $_ENV element
Shortcut for getting env variables
session($key)
Parameters
• $key (string) – the key that will be taken from the $_SESSION array
Returns array the value in the $_SESSION element
Shortcut for getting session variables
cookie($key)
Parameters
• $key (string) – the key that will be taken from the $_COOKIE array
Returns array the value in the $_COOKIE element
Shortcut for getting cookie variables
render($templateName, $params=array(), $renderAs=’user’, $headers=array())
Parameters
• $templateName (string) – the name of the template
• $params (array) – the template parameters in key => value structure
• $renderAs (string) – user renders a full page, blank only your template admin an entry in
the admin settings
• $headers (array) – set additional headers in name/value pairs
Returns \OCA\AppFramework\Http\TemplateResponse containing the page
Shortcut for rendering a template
renderJSON($data=array(), $errorMsg=null)
Parameters
• $data (array) – the PHP array that will be put into the JSON data indexempty by default
• $errorMsg (string) – If you want to return an error message, pass one
Returns \OCA\AppFramework\Http\JSONResponse containing the values
Shortcut for rendering a JSON response
API
This is used to wrap the owncloud static api calls into an object to make the code better abstractable for use in the
dependency injection container Should you find yourself in need for more methods, simply inherit from this class and
add your methods
class OCA\AppFramework\Core\API
__construct($appName)
Parameters
98
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• $appName (string) – the name of your application
constructor
getAppName()
Returns string the name of your application
used to return the appname of the set application
addNavigationEntry($entry)
Parameters
• $entry (array) – containing: id, name, order, icon and href key
Creates a new navigation entry
getUserId()
Returns string the user id of the current user
Gets the userid of the current user
activateNavigationEntry()
Sets the current navigation entry to the currently running app
addScript($scriptName, $appName=null)
Parameters
• $scriptName (string) – the name of the javascript in js/ without the suffix
• $appName (string) – the name of the app, defaults to the current one
Adds a new javascript file
addStyle($styleName, $appName=null)
Parameters
• $styleName (string) – the name of the css file in css/without the suffix
• $appName (string) – the name of the app, defaults to the current one
Adds a new css file
add3rdPartyScript($name)
Parameters
• $name (string) – the name of the file without the suffix
shorthand for addScript for files in the 3rdparty directory
add3rdPartyStyle($name)
Parameters
• $name (string) – the name of the file without the suffix
shorthand for addStyle for files in the 3rdparty directory
getSystemValue($key)
Parameters
• $key (string) – the key of the value, under which it was saved
Returns string the saved value
3.4. App Development (App Framework)
99
ownCloud Developer Manual, Release 6.0
Looks up a systemwide defined value
setSystemValue($key, $value)
Parameters
• $key (string) – the key of the value, under which will be saved
• $value (string) – the value that should be stored
Sets a new systemwide value
getAppValue($key, $appName=null)
Parameters
• $key (string) – the key of the value, under which it was saved
• $appName (mixed) –
Returns string the saved value
Looks up an appwide defined value
setAppValue($key, $value, $appName=null)
Parameters
• $key (string) – the key of the value, under which will be saved
• $value (string) – the value that should be stored
• $appName (mixed) –
Writes a new appwide value
setUserValue($key, $value, $userId=null)
Parameters
• $key (string) – the key under which the value is being stored
• $value (string) – the value that you want to store
• $userId (string) – the userId of the user that we want to store the value under, defaults to
the current one
Shortcut for setting a user defined value
getUserValue($key, $userId=null)
Parameters
• $key (string) – the key under which the value is being stored
• $userId (string) – the userId of the user that we want to store the value under, defaults to
the current one
Shortcut for getting a user defined value
getTrans()
Returns \OC_L10N the translation object
Returns the translation object
prepareQuery($sql, $limit=null, $offset=null)
Parameters
• $sql (string) – the sql query with ? placeholder for params
100
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• $limit (int) – the maximum number of rows
• $offset (int) – from which row we want to start
Returns \OCP\DB a query object
Used to abstract the owncloud database access away
getInsertId($tableName)
Parameters
• $tableName (string) – the name of the table where we inserted the item
Returns int the id of the inserted element
Used to get the id of the just inserted element
linkToRoute($routeName, $arguments=array())
Parameters
• $routeName (string) – the name of the route
• $arguments (array) – an array with arguments which will be filled into the url
Returns string the url
Returns the URL for a route
linkTo($file, $appName=null)
Parameters
• $file (string) – the name of the file
• $appName (string) – the name of the app, defaults to the current one
Returns an URL for an image or file
imagePath($file, $appName=null)
Parameters
• $file (string) – the name of the file
• $appName (string) – the name of the app, defaults to the current one
Returns the link to an image, like link to but only with prepending img/
getAbsoluteURL($url)
Parameters
• $url (string) – the url
Returns string the absolute url
Makes an URL absolute
linkToAbsolute($file, $appName=null)
Parameters
• $file (string) – the name of the file
• $appName (string) – the name of the app, defaults to the current one
Returns string the url
3.4. App Development (App Framework)
101
ownCloud Developer Manual, Release 6.0
Warning: DEPRECATED: replaced with linkToRoute()
links to a file
isLoggedIn()
Returns bool true if logged in
Checks if the current user is logged in
isAdminUser($userId)
Parameters
• $userId (string) – the id of the user
Returns bool true if admin
Checks if a user is an admin
isSubAdminUser($userId)
Parameters
• $userId (string) – the id of the user
Returns bool true if subadmin
Checks if a user is an subadmin
passesCSRFCheck()
Returns bool true if CSRF check passed
Checks if the CSRF check was correct
isAppEnabled($appName)
Parameters
• $appName (string) – the name of an app
Returns bool true if app is enabled
Checks if an app is enabled
log($msg, $level=null)
Parameters
• $msg (string) – the error message to be logged
• $level (int) – the error level
Writes a function into the error log
getTemplate($templateName, $renderAs=’user’, $appName=null)
Parameters
• $templateName (string) – the name of the template
• $renderAs (string) – how it should be rendered
• $appName (string) – the name of the app
Returns \OCP\Template a new template
Returns a template
102
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
getLocalFilePath($path)
Parameters
• $path (string) – path the path to the file on the oc filesystem
Returns string the filepath in the filesystem
turns an owncloud path into a path on the filesystem
openEventSource()
Returns \OC_EventSource a new open EventSource class
used to return and open a new eventsource
connectHook($signalClass, $signalName, $slotClass, $slotName)
Parameters
• $signalClass (string) – class name of emitter
• $signalName (string) – name of signal
• $slotClass (string) – class name of slot
• $slotName (string) – name of slot, in another word, this is the name of the method that
will be called when registered signal is emitted.
Returns \OCA\AppFramework\Core\bool, always true
connects a function to a hook
emitHook($signalClass, $signalName, $params=array())
Parameters
• $signalClass (string) – class name of emitter
• $signalName (string) – name of signal
• $params (array) – defautl: array() array with additional data
Returns \OCA\AppFramework\Core\bool, true if slots exists or false if not
Emits a signal. To get data from the slot use references!
clearHook($signalClass=false, $signalName=false)
Parameters
• $signalClass (string) –
• $signalName (string) –
clear hooks
getUrlContent($url)
Parameters
• $url (string) – the url that should be fetched
Returns string the content of the webpage
Gets the content of an URL by using CURL or a fallback if it is notinstalled
addRegularTask($className, $methodName)
Parameters
3.4. App Development (App Framework)
103
ownCloud Developer Manual, Release 6.0
• $className (string) – full namespace and class name of the class
• $methodName (string) – the name of the static method that should becalled
Register a backgroundjob task
registerAdmin($mainPath, $appName=null)
Parameters
• $mainPath (string) – the path to the main php file without the phpsuffix, relative to your
apps directory! not the template directory
• $appName (string) – the name of the app, defaults to the current one
Tells ownCloud to include a template in the admin overview
DoesNotExistException
This is returned or should be returned when a find request does not find an entry in the database
class OCA\AppFramework\Db\DoesNotExistException
__construct($msg)
Parameters
• $msg (string) – the error message
Constructor
MultipleObjectsReturnedException
This is returned or should be returned when a find request finds more than one row
class OCA\AppFramework\Db\MultipleObjectsReturnedException
__construct($msg)
Parameters
• $msg (string) – the error message
Constructor
Mapper
Simple parent class for inheriting your data access layer from. This class may be subject to change in the future
class OCA\AppFramework\Db\Mapper
•Abstract
__construct($api, $tableName)
Parameters
• $api (\OCA\AppFramework\Core\API) – Instance of the API abstraction layer
• $tableName (string) – the name of the table. set this to allow entity queries without using
sql
104
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
getTableName()
Returns string the table name
delete($entity)
Parameters
• $entity (\OCA\AppFramework\Db\Entity) –
Deletes an entity from the table
insert($entity)
Parameters
• $entity (\OCA\AppFramework\Db\Entity) –
Returns \OCA\AppFramework\Db\the saved entity with the set id
Creates a new entry in the db from an entity
update($entity)
Parameters
• $entity (\OCA\AppFramework\Db\Entity) –
Throws \InvalidArgumentException if entity has no id
Updates an entry in the db from an entity
findOneQuery($sql, $params)
Parameters
• $sql (string) – the sql query
• $params (array) – the parameters of the sql query
Throws \OCA\AppFramework\Db\DoesNotExistException if the item does not exist
Returns array the result as row
•Protected
Returns an db result and throws exceptions when there are more or lessresults
execute($sql, $params=array(), $limit=null, $offset=null)
Parameters
• $sql (string) – the prepare string
• $params (array) – the params which should replace the ? in the sql query
• $limit (int) – the maximum number of rows
• $offset (int) – from which row we want to start
Returns \PDOStatement the database query result
•Protected
Runs an sql query
3.4. App Development (App Framework)
105
ownCloud Developer Manual, Release 6.0
Entity
class OCA\AppFramework\Db\Entity
•Abstract
property $id
static Entity::fromParams($params)
Parameters
• $params (array) – the array which was obtained via $this->params(‘key’)in the controller
Returns \OCA\AppFramework\Db\Entity
Simple alternative constructor for building entities from a request
fromRow($row)
Parameters
• $row (array) – the row to map onto the entity
Maps the keys of the row array to the attributes
resetUpdatedFields()
Marks the entity as clean needed for setting the id after the insertion
__call($methodName, $args)
Parameters
• $methodName (mixed) –
• $args (mixed) –
Each time a setter is called, push the part after setinto an array: for instance setId will save Id in theupdated
fields array so it can be easily used to create thegetter method
markFieldUpdated($attribute)
Parameters
• $attribute (string) – the name of the attribute
•Protected
Mark am attribute as updated
columnToProperty($columnName)
Parameters
• $columnName (string) – the name of the column
Returns string the property name
Transform a database columnname to a property
propertyToColumn($property)
Parameters
• $property (string) – the name of the property
Returns string the column name
Transform a property to a database column name
106
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
getUpdatedFields()
Returns array array of updated fields for update query
addType($fieldName, $type)
Parameters
• $fieldName (string) – the name of the attribute
• $type (string) – the type which will be used to call settype()
•Protected
Adds type information for a field so that its automatically casted tothat value once its being returned from
the database
DIContainer
This class extends Pimple (http://pimple.sensiolabs.org/) for reusability To use this class, extend your own container
from this. Should you require it you can overwrite the dependencies with your own classes by simply redefining a
dependency
class OCA\AppFramework\DependencyInjection\DIContainer
__construct($appName)
Parameters
• $appName (string) – the name of the app
Put your class dependencies in here
Dispatcher
Class to dispatch the request to the middleware disptacher
class OCA\AppFramework\Http\Dispatcher
__construct($protocol, $middlewareDispatcher)
Parameters
• $protocol (\OCA\AppFramework\Http\Http) – the http protocol with contains all status
headers
• $middlewareDispatcher (\OCA\AppFramework\Middleware\MiddlewareDispatcher) –
the dispatcher which runs the middleware
dispatch($controller, $methodName)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller which will be
called
• $methodName (string) – the method name which will be called onthe controller
Returns array $array[0] contains a string with the http main header,$array[1] contains headers
in the form: $key => value, $array[2] containsthe response output
3.4. App Development (App Framework)
107
ownCloud Developer Manual, Release 6.0
Handles a request and calls the dispatcher on the controller
Http
class OCA\AppFramework\Http\Http
property $headers
•Protected
__construct($server, $protocolVersion=’HTTP/1.1’)
Parameters
• $server (mixed) –
• $protocolVersion (string) – the http version to use defaults to HTTP/1.1
getStatusHeader($status, $lastModified=null, $ETag=null)
:param \OCA\AppFramework\Http\Http::CONSTANT $status: the constant from the Http class :param
\DateTime $lastModified: formatted last modified date :param mixed $ETag:
Gets the correct header
Response
Baseclass for responses. Also used to just send headers
class OCA\AppFramework\Http\Response
cacheFor($cacheSeconds)
Parameters
• $cacheSeconds (int) – the amount of seconds that should be cachedif 0 then caching will
be disabled
Caches the response
addHeader($name, $value)
Parameters
• $name (string) – The name of the HTTP header
• $value (string) – The value, null will delete it
Adds a new header to the response that will be called before the renderfunction
getHeaders()
Returns array the headers
Returns the set headers
render()
Returns null
By default renders no output
setStatus($status)
Parameters
108
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• $status (int) – a HTTP status code, see also the STATUS constants
Set response status
getStatus()
Get response status
getETag()
Returns string the etag
getLastModified()
Returns string RFC2822 formatted last modified date
setETag($ETag)
Parameters
• $ETag (string) –
setLastModified($lastModified)
Parameters
• $lastModified (\DateTime) –
DownloadResponse
Prompts the user to download the a file
class OCA\AppFramework\Http\DownloadResponse
•Abstract
__construct($filename, $contentType)
Parameters
• $filename (string) – the name that the downloaded file should have
• $contentType (string) – the mimetype that the downloaded file should have
Creates a response that prompts the user to download the file
JSONResponse
A renderer for JSON calls
class OCA\AppFramework\Http\JSONResponse
property $error
•Protected
property $data
•Protected
__construct()
setParams($params)
Parameters
3.4. App Development (App Framework)
109
ownCloud Developer Manual, Release 6.0
• $params (array) – an array with key => value structure which will be transformed to
JSON
Sets values in the data json array
getParams()
Returns array the params
Used to get the set parameters
setErrorMessage($msg)
Parameters
• $msg (mixed) –
in case we want to render an error message, also logs into the owncloud log
render()
Returns string the rendered json
Returns the rendered json
RedirectResponse
Redirects to a different URL
class OCA\AppFramework\Http\RedirectResponse
__construct($redirectURL)
Parameters
• $redirectURL (string) – the url to redirect to
Creates a response that redirects to a url
getRedirectURL()
Returns string the url to redirect
TemplateResponse
Response for a normal template
class OCA\AppFramework\Http\TemplateResponse
property $templateName
•Protected
property $params
•Protected
property $api
•Protected
property $renderAs
•Protected
110
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
property $appName
•Protected
__construct($api, $templateName, $appName=null)
Parameters
• $api (\OCA\AppFramework\Core\API) – an API instance
• $templateName (string) – the name of the template
• $appName (string) – optional if you want to include a template from a different app
setParams($params)
Parameters
• $params (array) – an array with key => value structure which sets template variables
Sets template parameters
getParams()
Returns array the params
Used for accessing the set parameters
getTemplateName()
Returns string the name of the used template
Used for accessing the name of the set template
renderAs($renderAs)
Parameters
• $renderAs (string) – admin, user or blank. Admin also prints the admin settings header
and footer, user renders the normal normal page including footer and header and blank just
renders the plain template
Sets the template page
getRenderAs()
Returns string the renderAs value
Returns the set renderAs
render()
Returns string the rendered html
Returns the rendered html
TextResponse
Just outputs text to the browser
class OCA\AppFramework\Http\TextResponse
__construct($content, $contentType=’plain’)
Parameters
• $content (string) – the content that should be written into the file
3.4. App Development (App Framework)
111
ownCloud Developer Manual, Release 6.0
• $contentType (string) – the mimetype. text/ is added automatically soonly plain or html
can be added to get text/plain or text/html
Creates a response that just outputs text
render()
Returns string the file contents
Simply sets the headers and returns the file contents
TextDownloadResponse
Prompts the user to download the a textfile
class OCA\AppFramework\Http\TextDownloadResponse
__construct($content, $filename, $contentType)
Parameters
• $content (string) – the content that should be written into the file
• $filename (string) – the name that the downloaded file should have
• $contentType (string) – the mimetype that the downloaded file should have
Creates a response that prompts the user to download a file whichcontains the passed string
render()
Returns string the file contents
Simply sets the headers and returns the file contents
TwigResponse
Response for twig templates. Do not use this directly to render your templates, unless you want a blank page because
the owncloud header and footer won’t be included
class OCA\AppFramework\Http\TwigResponse
__construct($api, $templateName, $twig)
Parameters
• $api (\OCA\AppFramework\Core\API) – an api instance
• $templateName (string) – the name of the twig template
• $twig (\OCA\AppFramework\Http\Twig_Environment) – an instance of the twig environment for rendering
Instantiates the Twig Template
render()
Returns string rendered output
Returns the rendered result
112
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Request
Class for accessing variables in the request. This class provides an immutable object with request variables.
class OCA\AppFramework\Http\Request
property $items
•Protected
property $allowedKeys
•Protected
__construct($vars=array())
Parameters
• $vars (array) – An associative array with the following optional values:
Note: OCAAppFrameworkHttphttp://www.php.net/manual/en/reserved.variables.php
count()
offsetExists($offset)
Parameters
• $offset (string) – offset The key to lookup
Returns string|null
ArrayAccess methods Gives access to the combined GET, POST and urlParams arraysExamples:$var =
$request[’myvar’];orif(!isset($request[’myvar’]) { // Do something}$request[’myvar’] = ‘something’; //
This throws an exception.
offsetGet($offset)
Parameters
• $offset (mixed) –
Note: OCAAppFrameworkHttpoffsetExists
offsetSet($offset, $value)
Parameters
• $offset (mixed) –
• $value (mixed) –
Note: OCAAppFrameworkHttpoffsetExists
offsetUnset($offset)
Parameters
• $offset (mixed) –
3.4. App Development (App Framework)
113
ownCloud Developer Manual, Release 6.0
Note: OCAAppFrameworkHttpoffsetExists
__set($name, $value)
Parameters
• $name (mixed) –
• $value (mixed) –
__get($name)
Parameters
• $name (string) – The key to look for.
Returns mixed|null
Access request variables by method and name. Examples:$request->post[’myvar’]; // Only look for POST
variables$request->myvar; or $request->{‘myvar’}; or $request->{$myvar}Looks in the combined GET,
POST and urlParams array.if($request->method !== ‘POST’) { throw new Exception(‘This function can
only be invoked using POST’);}
__isset($name)
Parameters
• $name (mixed) –
__unset($id)
Parameters
• $id (mixed) –
NotFoundResponse
Pure hader response, Just return 404 status to the browser
class OCA\AppFramework\Http\NotFoundResponse
__construct()
Creates a response that just returns 404 status
ForbiddenResponse
Pure hader response, Just return 403 status to the browser
class OCA\AppFramework\Http\ForbiddenResponse
__construct()
Creates a response that just returns 403 status
114
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Middleware
Middleware is used to provide hooks before or after controller methods and deal with possible exceptions raised in the controller methods.
They’re modeled after Django’s middleware system:
https://docs.djangoproject.com/en/dev/topics/http/middleware/
class OCA\AppFramework\Middleware\Middleware
•Abstract
beforeController($controller, $methodName)
Parameters
• $controller (\OCA\AppFramework\Middleware\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
This is being run in normal order before the controller is beingcalled which allows several modifications
and checks
afterException($controller, $methodName, $exception)
Parameters
• $controller (\OCA\AppFramework\Middleware\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $exception (\Exception) – the thrown exception
Returns \OCA\AppFramework\Http\Response a Response object or null in case that the exception could not be handled
This is being run when either the beforeController method or thecontroller method itself is throwing an
exception. The middleware isasked in reverse order to handle the exception and to return a response.If the
response is null, it is assumed that the exception could not behandled and the error will be thrown again
afterController($controller, $methodName, $response)
Parameters
• $controller (\OCA\AppFramework\Middleware\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $response (\OCA\AppFramework\Http\Response) – the generated response from the controller
Returns \OCA\AppFramework\Http\Response a Response object
This is being run after a successful controllermethod call and allowsthe manipulation of a Response object.
The middleware is run in reverse order
beforeOutput($controller, $methodName, $output)
Parameters
• $controller (\OCA\AppFramework\Middleware\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $output (string) – the generated output from a response
3.4. App Development (App Framework)
115
ownCloud Developer Manual, Release 6.0
Returns string the output that should be printed
This is being run after the response object has been rendered andallows the manipulation of the output.
The middleware is run in reverse order
MiddlewareDispatcher
This class is used to store and run all the middleware in correct order
class OCA\AppFramework\Middleware\MiddlewareDispatcher
__construct()
Constructor
registerMiddleware($middleware)
Parameters
• $middleware (\OCA\AppFramework\Middleware\Middleware) – the middleware which
will be added
Adds a new middleware
getMiddlewares()
Returns array the middlewares
returns an array with all middleware elements
beforeController($controller, $methodName)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
This is being run in normal order before the controller is beingcalled which allows several modifications
and checks
afterException($controller, $methodName, $exception)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $exception (\Exception) – the thrown exception
Returns \OCA\AppFramework\Http\Response a Response object or null in case that the exception could not behandled
This is being run when either the beforeController method or thecontroller method itself is throwing an
exception. The middleware is askedin reverse order to handle the exception and to return a response.If the
response is null, it is assumed that the exception could not behandled and the error will be thrown again
afterController($controller, $methodName, $response)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
116
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
• $methodName (string) – the name of the method that will be called on the controller
• $response (\OCA\AppFramework\Http\Response) – the generated response from the controller
Returns \OCA\AppFramework\Http\Response a Response object
This is being run after a successful controllermethod call and allowsthe manipulation of a Response object.
The middleware is run in reverse order
beforeOutput($controller, $methodName, $output)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $output (string) – the generated output from a response
Returns string the output that should be printed
This is being run after the response object has been rendered andallows the manipulation of the output.
The middleware is run in reverse order
SecurityMiddleware
Used to do all the authentication and checking stuff for a controller method It reads out the annotations of a controller
method and checks which if security things should be checked and also handles errors in case a security check fails
class OCA\AppFramework\Middleware\Security\SecurityMiddleware
__construct($api)
Parameters
• $api (\OCA\AppFramework\Core\API) – an instance of the api
beforeController($controller, $methodName)
Parameters
• $controller (\OCA\AppFramework\Middleware\Security\string/Controller) – the controllername or string
• $methodName (string) – the name of the method
Throws \OCA\AppFramework\Middleware\Security\SecurityException when a security
check fails
This runs all the security checks before a method call. Thesecurity checks are determined by inspecting
the controller methodannotations
afterException($controller, $methodName, $exception)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $exception (\Exception) – the thrown exception
3.4. App Development (App Framework)
117
ownCloud Developer Manual, Release 6.0
Returns \OCA\AppFramework\Http\Response a Response object or null in case that the exception could not be handled
If an SecurityException is being caught, ajax requests return a JSON errorresponse and non ajax requests
redirect to the index
SecurityException
Thrown when the security middleware encounters a security problem
class OCA\AppFramework\Middleware\Security\SecurityException
__construct($msg, $ajax)
Parameters
• $msg (string) – the security error message
• $ajax (bool) – true if it resulted because of an ajax request
isAjax()
Returns bool true if exception resulted because of an ajax request
Used to check if a security exception occured in an ajax request
TwigMiddleware
This template is used to add the possibility to add twig templates By default it is only loaded when the templatepath is
set
class OCA\AppFramework\Middleware\Twig\TwigMiddleware
__construct($api, $twig)
Parameters
• $api (\OCA\AppFramework\Core\API) – an instance of the api
• $twig (\OCA\AppFramework\Middleware\Twig\Twig_Environment) – an instance of the
twig environment
Sets the twig loader instance
afterController($controller, $methodName, $response)
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $response (\OCA\AppFramework\Http\Response) – the generated response from the controller
Returns \OCA\AppFramework\Http\Response a Response object
Swaps the template response with the twig response and stores if atemplate needs to be printed for the user
or admin page
beforeOutput($controller, $methodName, $output)
118
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Parameters
• $controller (\OCA\AppFramework\Controller\Controller) – the controller that is being
called
• $methodName (string) – the name of the method that will be called on the controller
• $output (string) – the generated output from a response
Returns string the output that should be printed
In case the output is not rendered as blank page, we need to include theowncloud header and output
ControllerTestUtility
Simple utility class for testing controllers
class OCA\AppFramework\Utility\ControllerTestUtility
•Abstract
assertAnnotations($controller, $method, $expected, $valid=array())
Parameters
• $controller (\OCA\AppFramework\Utility\Controller/string) – name or instance of the
controller
• $method (mixed) –
• $expected (array) – an array containing the expected annotations
• $valid (array) – if you define your own annotations, pass them here
•Protected
Checks if a controllermethod has the expected annotations
assertHeaders($expected=array(), $response)
Parameters
• $expected (array) – an array with the expected headers
• $response (\OCA\AppFramework\Http\Response) – the response which we want to test
for headers
•Protected
Shortcut for testing expected headers of a response
getRequest($params)
Parameters
• $params (array) – a hashmap with the parameters for request
Returns \OCA\AppFramework\Http\Request a request instance
•Protected
Instead of using positional parameters this function instantiatesa request by using a hashmap so its easier
to only set specific params
3.4. App Development (App Framework)
119
ownCloud Developer Manual, Release 6.0
MapperTestUtility
Simple utility class for testing mappers
class OCA\AppFramework\Utility\MapperTestUtility
•Abstract
property $api
•Protected
beforeEach()
•Protected
Run this function before the actual test to either set or initialize theapi. After this the api can be accessed
by using $this->api
setMapperResult($sql, $arguments=array(), $returnRows=array(), $limit=null, $offset=null)
Parameters
• $sql (string) – the sql query that you expect to receive
• $arguments (array) – the expected arguments for the prepare querymethod
• $returnRows (array) – the rows that should be returned for the resultof the database query.
If not provided, it wont be assumed that fetchRowwill be called on the result
• $limit (mixed) –
• $offset (mixed) –
•Protected
Create mocks and set expected results for database queries
TestUtility
Simple utility class for testing anything using an api
class OCA\AppFramework\Utility\TestUtility
•Abstract
getAPIMock($apiClass=’OCAAppFrameworkCoreAPI’, $constructor=array(‘appname’))
Parameters
• $apiClass (string) – the class inclusive namespace of the api that we want to use
• $constructor (array) – constructor parameters of the api class
•Protected
Boilerplate function for getting an API Mock class
120
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
MethodAnnotationReader
Reads and parses annotations from doc comments
class OCA\AppFramework\Utility\MethodAnnotationReader
__construct($object, $method)
Parameters
• $object (object) – an object or classname
• $method (string) – the method which we want to inspect for annotations
hasAnnotation($name)
Parameters
• $name (string) – the name of the annotation
Returns bool true if the annotation is found
Check if a method contains an annotation
FaviconFetcher
class OCA\AppFramework\Utility\FaviconFetcher
__construct($apiFactory)
Parameters
• $apiFactory (\OCA\AppFramework\Utility\SimplePieAPIFactory) –
Inject a factory to build a simplepie file object. This is needed becausethe file object contains logic in its
constructor which makes itimpossible to inject and test
fetch($url)
Parameters
• $url (string|null) – the url where to fetch it from
Fetches a favicon from a given URL
extractFromPage($url)
Parameters
• $url (string) – the url to the page
Returns string the full url to the page
•Protected
Tries to get a favicon from a page
isImage($url)
Parameters
• $url (string) – the url to the file
Returns bool true if image
3.4. App Development (App Framework)
121
ownCloud Developer Manual, Release 6.0
•Protected
Test if the file is an image
buildURL($url)
Parameters
• $url (string) – the url that should be built
Returns array an array containing the http and https address
•Protected
Get HTTP and HTTPS adresses from an incomplete URL
SimplePieAPIFactory
class OCA\AppFramework\Utility\SimplePieAPIFactory
getFile($url, $timeout=10, $redirects=5, $headers=null, $useragent=null, $force_fsockopen=false)
Parameters
• $url (mixed) –
• $timeout (mixed) –
• $redirects (mixed) –
• $headers (mixed) –
• $useragent (mixed) –
• $force_fsockopen (mixed) –
Returns \OCA\AppFramework\Utility\SimplePie_File a new object
Builds a simplepie file object. This is needed becausethe file object contains logic in its constructor which
makes itimpossible to inject and test
getCore()
Returns \SimplePie_Core instance
Returns a new instance of a SimplePie_Core() object. This is neededbecause the class relies on external dependencies which are not passedin via the constructor and thus making it nearly impossible to unittestcode
that uses this class
TimeFactory
Needed to mock calls to time()
class OCA\AppFramework\Utility\TimeFactory
getTime()
Returns int the result of a call to time()
122
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
Main
• App
• DIContainer
API Layer
• API
Request
• Dispatcher
• Http
• Request
Controllers
• Controller
Database
• DoesNotExistException
• MultipleObjectsReturnedException
• Mapper
Responses
• Response
• DownloadResponse
• JSONResponse
• RedirectResponse
• TemplateResponse
• TextResponse
• TextDownloadResponse
Middleware
• Middleware
• MiddlewareDispatcher
3.4. App Development (App Framework)
123
ownCloud Developer Manual, Release 6.0
Security & Authentication
• SecurityMiddleware
• MethodAnnotationReader
• SecurityException
Twig Templates
• TwigMiddleware
• TwigResponse
Utilities
• FaviconFetcher
• SimplePieAPIFactory
• TimeFactory
Testing
• ControllerTestUtility
• MapperTestUtility
• TestUtility
3.5 Intro
Before you start, please check if there already is a similar app you could contribute to. Also, feel free to communicate
your idea and plans to the user mailing list or developer mailing list so other contributors might join in.
Then, please make sure you have set up a development environment:
• Development Environment
Before starting to write an app please read the security and coding guidelines:
• Security Guidelines
• Coding Style & General Guidelines
After this you can start to write your app:
• Creating An App
3.6 App Development using ownCloud App API
You can choose between the traditional and MVC style (App Framework) approach. For a comparison see App
Framework Comparison. This approach uses the basic ownCloud libraries and provides no classes to use for MVC
development and testing.
• App Tutorial
124
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
3.6.1 General
Inner parts of an app
• Classloader
• Routes
• App Metadata
• Debugging
3.6.2 Database
Database access
• Database Schema | Database Access
3.6.3 Templates
HTML and inclusion of JavaScript and CSS
• Templates
3.6.4 JavaScript & CSS
• Static content
• CSS
3.6.5 Testing
• Acceptance Tests
3.6.6 API Documentation
• ownCloud App API
3.7 App Development using the App Framework App
Develop an app using an MVC Framework. The App Framework provides enhanced Security, MVC classes and
testing tools but you need to read more until you can produce the first output.
• App Tutorial
3.7. App Development using the App Framework App
125
ownCloud Developer Manual, Release 6.0
3.7.1 General
Inner parts of an app
• Classloader
• Runtime configuration | Dependency Injection
• Routes
• App Metadata
• Debugging
3.7.2 Controllers
Contain the logic for each request
• Controllers
3.7.3 Database
Database access
• Database Schema | Database Access
3.7.4 Templates
HTML and inclusion of JavaScript and CSS
• Templates
3.7.5 JavaScript & CSS
• Static content
• AngularJS | Angular Setup | Angular App Framework Libraries
• CSS
3.7.6 Middleware
Hook before or after controller execution
• Middleware
3.7.7 Testing
• Unittests
• Acceptance Tests
126
Chapter 3. App Development
ownCloud Developer Manual, Release 6.0
3.7.8 API Documentation
• AppFramework API
• ownCloud App API
3.8 Additional APIs
Can be used with and without App Framework
• Data Migration
• Hooks
• Filesystem
3.8. Additional APIs
127
ownCloud Developer Manual, Release 6.0
128
Chapter 3. App Development
CHAPTER
FOUR
CORE DEVELOPMENT
4.1 Translation
4.1.1 Make text translatable
In HTML or PHP wrap it like this <?php p($l->t(’This is some text’));?> or this <?php
print_unescaped($l->t(’This is some text’));?> For the right date format use <?php
p($l->l(’date’, time()));?>. Change the way dates are shown by editing /core/l10n/l10n-[lang].php To
translate text in javascript use: t(’appname’,’text to translate’);
Note: print_unescaped() should be preferred only if you would like to display HTML code. Otherwise, using
p() is strongly preferred to escape HTML characters against XSS attacks.
4.1.2 You shall never split sentences!
Reason:
Translators lose the context and they have no chance to possible re-arrange words.
Example:
<?php p($l->t(’Select file from’)) . ’ ’; ?><a href=’#’ id="browselink"><?php p($l->t(’local filesyst
Translators will translate:
• Select file from
• local filesystem
• ‘ or “
• cloud
By translating these individual strings for sure the case will be lost of local filesystem and cloud. The two white spaces
with the or will get lost while translating as well.
129
ownCloud Developer Manual, Release 6.0
Html on translation string:
Html tags in translation strings is ugly but usually translators can handle this.
What about variable in the strings?
In case you need to add variables to the translation strings do it like that:
$l->t(’%s is available. Get <a href="%s">more information</a>’, array($data[’versionstring’], $data[’
4.1.3 Automated synchronization of translations
Multiple nightly jobs have been setup in order to synchronize translations - it’s a multi-step process: perl l10n.pl
read will rescan all php and javascript files and generate the templates. The templates are pushed to Transifex (tx
push -s). All translations are pulled from Transifex (tx pull -a). perl l10n.pl write will write the php files
containing the translations. Finally the changes are pushed to git.
Please follow the steps below to add translation support to your app:
Create a folder l10n. Create the file ignorelist which can contain files which shall not be scanned during step 4.
Edit l10n/.tx/config and copy/past a config section and adopt it by changing the app/folder name. Run perl
l10n.pl read with l10n Add the newly created translation template (l10n/Templates/<appname>.pot) to git and
commit the changes above. After the next nightly sync job a new resource will appear on Transifex and from now on
every night the latest translations will arrive.
Translation sync jobs:
https://ci.owncloud.org/view/translation-sync/
Caution: information below is in general not needed!
4.1.4 Manual quick translation update:
cd l10n/ && perl l10n.pl read && tx push -s && tx pull -a && perl l10n.pl write && cd ..
The translation script requires Locale::PO, installable via apt-get install liblocale-po-perl
4.1.5 Configure transifex
tx init
for resource in calendar contacts core files media gallery settings
do
tx set --auto-local -r owncloud.$resource "<lang>/$resource.po" --source-language=en \
--source-file "templates/$resource.pot" --execute
done
130
Chapter 4. Core Development
ownCloud Developer Manual, Release 6.0
4.2 Unit-Testing
4.2.1 Getting PHPUnit
ownCloud uses PHPUnit >= 3.7 for unit testing.
To install it, either get it via your packagemanager:
sudo apt-get install phpunit
or install it via PEAR:
pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit
After the installation the ‘’phpunit” command is available.
4.2.2 Writing unit tests
To get started, do the following:
• Create a directory called tests in the top level of your application
• Create a php file in the directory and require_once your class which you want to test.
Then you can simply run the created test with phpunit.
Note: If you use owncloud functions in your class under test (i.e: OC::getUser()) you’ll need to bootstrap owncloud
or use dependency injection.
Note: You’ll most likely run your tests under a different user than the webserver. This might cause problems with
your PHP settings (i.e: open_basedir) and requires you to adjust your configuration.
An example for a simple test would be:
/srv/http/owncloud/apps/myapp/tests/testsuite.php
<?php
require_once("../myfolder/myfunction.php");
class TestAddTwo extends PHPUnit_Framework_TestCase {
public function testAddTwo(){
$this->assertEquals(5, addTwo(3));
}
}
?>
/srv/http/owncloud/apps/myapp/tests/testsuite.php
<?php
function addTwo($number){
return $number + 2;
}
?>
4.2. Unit-Testing
131
ownCloud Developer Manual, Release 6.0
In /srv/http/owncloud/apps/myapp/ you run the test with:
phpunit tests/testsuite.php
For more resources on PHPUnit visit: http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html
4.2.3 Bootstrapping ownCloud
If you use ownCloud functions or classes in your code, you’ll need to make them available to your test by bootstrapping
ownCloud.
To do this, you’ll need to provide the --bootstrap argument when running PHPUnit
/srv/http/owncloud:
phpunit --bootstrap tests/bootstrap.php apps/myapp/tests/testsuite.php
If you run the test under a different user than your webserver, you’ll have to adjust your php.ini and file rights.
/etc/php/php.ini:
open_basedir = none
/srv/http/owncloud:
su -c "chmod a+r config/config.php"
su -c "chmod a+rx data/"
su -c "chmod a+w data/owncloud.log"
4.2.4 Running unit tests for the ownCloud core project
The core project provides a script that runs all the core unit tests using different database backends like sqlite, mysql,
pgsql, oci (for Oracle):
./autotest.sh
To run tests only for sqlite:
./autotest.sh sqlite
To run a specific test suite (note that the test file path is relative to the “tests” directory):
./autotest.sh sqlite lib/share/share.php
4.2.5 Further Reading
• http://googletesting.blogspot.de/2008/08/by-miko-hevery-so-you-decided-to.html
• http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html
• http://www.youtube.com/watch?v=4E4672CS58Q&feature=bf_prev&list=PLBDAB2BA83BB6588E
• Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin)
132
Chapter 4. Core Development
ownCloud Developer Manual, Release 6.0
4.3 Theming ownCloud
Themes can be used to customize the look and feel of ownCloud. Themes can relate to the following topics of
owncloud:
• Theming the web-frontend
• Theming the owncloud Desktop client
This documentation contains only the Web-frontend adaptions so far.
4.3.1 Getting started
A good idea getting starting with a dynamically created website is to inspect it with webdeveloper tools, that are found
in almost any browser. They show the generated HTML and the CSS Code, that the client/browser is recieving: With
this facts you can easyly determine, where the following object-related attributes for the phenomenons are settled:
• place
• colour
• links
• graphics
In owncloud standard theme everything is held very simple. This allows you quick adpating. In an unchanged ownCloud version css files and the standard pictures reside in /owncloud/themes/default folder. The next thing you should
do, before starting any changes is: Make a backup of your current theme(s) e.g.:
• Goto . . . /owncloud/themes
• cp -r default default.old
4.4 Creating and activating a new theme
There are two basic ways of creating new themings:
• Doing all new from scratch
• Starting from an existing theme and doing everything step by step and more experimentally
Depending on how you created your new theme it will be necessary to
• put a new theme into the /themes -folder. The theme can be activated by putting “theme” => ‘themename’, into
the config.php file.
• make all changes in the /themes/default -file
4.5 Structure
The folder structure of a theme is exactly the same as the main ownCloud structure. You can override js files, images
and templates with own versions. css files are loaded additionally to the default files so you can override css properties.
4.3. Theming ownCloud
133
ownCloud Developer Manual, Release 6.0
4.6 How to change images and the logo
A new logo which you may want to insert can be added as follows:
4.6.1 Figure out the path of the old logo
Replace the old pictue, which postition you found out as described under 1.3. by adding an extension in case you want
to re-use it later.
4.6.2 Creating an own logo
If you want to do a quick exchange like (1) it’s important to know the size of the picture before you start creating an
own logo:
• Go to the place in the filesystem, that has been shown by the webdeveloper tool/s
• You can look up sizing in most cases via the file properties inside your file-manager
• Create an own picture/logo with the same size then
The (main) pictures, that can be found inside ownCloud standard theming are the following:
• The logo at the login-page above the credentials-box: . . . /owncloud/themes/default/core/img/logo.svg
• The logo, that’s always in the left upper corner after login: . . . /owncloud/themes/default/core/img/logo-wide.svg
4.6.3 Inserting your new logo
Inserting a new logo into an existing theme is as simple as replacing the old logo with the new (genreated) one. You
can use: scalable vector graphics (.svg) or common graphics formats for the internet such as portable network graphics
(.png) or .jepg Just insert the new created picture by using the unchanged name of the old picture.
4.6.4 changing the default colours
With a web-developer tool like Mozilla-Inspector, you also get easyly displayed the color of the background you
klicked on. On the top of the login page you can see a case- destinguished setting for different browsers:
The different backround-assignements indicate the headers for a lot of different browser types. What you most likely
want to do is change the #35537a (lighter blue) and #ld2d42 (dark blue) color to the colours of our choice. In
some older and other browsers, there is just one color, but in the rest showing gradients is possible. The login page
background is a horizontal gradient. The first hex number, #35537a, is the top color of the gradient at the login screen.
The second hex number, #ld2d42 is the bottom color of the gradient at the login screen. The gradient in top of the
normal view after login is also defined by these css-settings, so that they take effect in logged in situation as well.
Change these colors to the hex color of your choice: As usual:
• the first two figures give the intensity of the red channel,
• the second two give the green intensity and the
• tird pair gives the blue value.
Save your css-file and refresh to see the new login screen. The other major color scheme is the blue header bar on the
main navigation page once you log in to ownCloud. This color we will change with the above as well. Save the file
and refresh the browser for the changes to take effect.
134
Chapter 4. Core Development
ownCloud Developer Manual, Release 6.0
4.7 Testing the new theme out
There are different options for doing so:
• If you’re using a tool like the Inspector tools inside Mozilla, you can test out the CSS-Styles immediately inside
the css-attributes, while looking at them.
• If you have a developing/testing server as desciribed in 1. you can test out the effects in a real environment
permanently.
4.8 Notes for Updates
In case of theming it is recommended to the user, not to perform these adaptions inside the folder /themes/default.
Please perform the following steps, to avoid conflicts with other upcoming updates:
• create a new folder inside /themes: for example: /themes/MyTheme
• Copy the folders /themes/default/core and /themes/default/settings to /themes/MyTheme
• edit the file /config/config.php
• Insert: ‘theme’ => ‘MyTheme’, into this file
Within the folder /themes/MyTheme all files, which are needed for theming are save from coming updates. All
company theming must be done exclusively here from now on.
In case, that one of the following files is affected due to an upgrade,
• /themes/default/settings/templates/apps.php
• /themes/default/defaults.php
the files listed below, have to be replaced by those listed above:
• /themes/MyTheme/settings/templates/apps.php
• /themes/MyTheme/defaults.php
But this is unlikely at least in the upcoming updates (5.0.x). ownCloud aims to give further information in this case.
4.9 App config
<?php
define("DEBUG", true);
$CONFIG = array(
/* Flag to indicate ownCloud is successfully installed (true = installed) */
"installed" => false,
/* Type of database, can be sqlite, mysql or pgsql */
"dbtype" => "sqlite",
/* Name of the ownCloud database */
"dbname" => "owncloud",
/* User to access the ownCloud database */
4.7. Testing the new theme out
135
ownCloud Developer Manual, Release 6.0
"dbuser" => "",
/* Password to access the ownCloud database */
"dbpassword" => "",
/* Host running the ownCloud database */
"dbhost" => "",
/* Prefix for the ownCloud tables in the database */
"dbtableprefix" => "",
/* Define the salt used to hash the user passwords. All your user passwords are lost if you lose this
"passwordsalt" => "",
/* Force use of HTTPS connection (true = use HTTPS) */
"forcessl" => false,
/* Theme to use for ownCloud */
"theme" => "",
/* Path to the 3rdparty directory */
"3rdpartyroot" => "",
/* URL to the 3rdparty directory, as seen by the browser */
"3rdpartyurl" => "",
/* Default app to load on login */
"defaultapp" => "files",
/* Enable the help menu item in the settings */
"knowledgebaseenabled" => true,
/* URL to use for the help page, server should understand OCS */
"knowledgebaseurl" => "http://api.apps.owncloud.com/v1",
/* Enable installing apps from the appstore */
"appstoreenabled" => true,
/* URL of the appstore to use, server should understand OCS */
"appstoreurl" => "http://api.apps.owncloud.com/v1",
/* Mode to use for sending mail, can be sendmail, smtp, qmail or php, see PHPMailer docs */
"mail_smtpmode" => "sendmail",
/* Host to use for sending mail, depends on mail_smtpmode if this is used */
"mail_smtphost" => "127.0.0.1",
/* authentication needed to send mail, depends on mail_smtpmode if this is used
* (false = disable authentication)
*/
"mail_smtpauth" => false,
/* Username to use for sendmail mail, depends on mail_smtpauth if this is used */
"mail_smtpname" => "",
/* Password to use for sendmail mail, depends on mail_smtpauth if this is used */
"mail_smtppassword" => "",
136
Chapter 4. Core Development
ownCloud Developer Manual, Release 6.0
/* Check 3rdparty apps for malicious code fragments */
"appcodechecker" => "",
/* Check if ownCloud is up to date */
"updatechecker" => true,
/* Place to log to, can be owncloud and syslog (owncloud is log menu item in admin menu) */
"log_type" => "owncloud",
/* File for the owncloud logger to log to, (default is ownloud.log in the data dir */
"logfile" => "",
/* Loglevel to start logging at. 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR (default is WARN) */
"loglevel" => "",
/* Lifetime of the remember login cookie, default is 15 days */
"remember_login_cookie_lifetime" => 60*60*24*15,
/* The directory where the user data is stored, default to data in the owncloud
* directory. The sqlite database is also stored here, when sqlite is used.
*/
// "datadirectory" => "",
"apps_paths" => array(
/* Set an array of path for your apps directories
key ’path’ is for the fs path and the key ’url’ is for the http path to your
applications paths. ’writable’ indicate if the user can install apps in this folder.
You must have at least 1 app folder writable or you must set the parameter : appstoreenabled to fals
*/
array(
’path’=> ’/var/www/owncloud/apps’,
’url’ => ’/apps’,
’writable’ => true,
),
),
);
4.9.1 Using alternative app directories
ownCloud can be set to use a custom app directory in /config/config.php. Customise the following code and add it to
your config file:
’apps_paths’ =>
array (
0 =>
array (
’path’ => OC::$SERVERROOT.’/apps’,
’url’ => ’/apps’,
’writable’ => true,
),
1 =>
array (
’path’ => OC::$SERVERROOT.’/apps2’,
’url’ => ’/apps2’,
’writable’ => false,
),
4.9. App config
137
ownCloud Developer Manual, Release 6.0
),
ownCloud will use the first app directory which it finds in the array with ‘writable’ set to true.
4.10 OCS Share API
The OCS Share API allows you to access the sharing API from outside over pre-defined OCS calls.
The base URL for all calls to the share API is: <owncloud_base_url>/ocs/v1.php/apps/files_sharing/api/v1
4.10.1 Get All Shares
Get all shares from the user.
• Syntax: /shares
• Method: GET
• Result: XML with all shares
Statuscodes:
• 100 - successful
• 404 - couldn’t fetch shares
4.10.2 Get Shares from a specific file or folder
Get all shares from a given file/folder.
• Syntax: /shares
• Method: GET
• URL Arguments: path - (string) path to file/folder
• URL Arguments: reshares - (boolean) returns not only the shares from the current user but all shares from the
given file.
• URL Arguments: subfiles - (boolean) returns all shares within a folder, given that path defines a folder
• Mandatory fields: path
• Result: XML with the shares
Statuscodes
• 100 - successful
• 400 - not a directory (if the ‘subfile’ argument was used)
• 404 - file doesn’t exist
4.10.3 Get information about a known Share
Get information about a given share.
• Syntax: /shares/<share_id>
• Method: GET
138
Chapter 4. Core Development
ownCloud Developer Manual, Release 6.0
• Arguments: share_id - (int) share ID
• Result: XML with the share information
Statuscodes:
• 100 - successful
• 404 - share doesn’t exist
4.10.4 Create a new Share
Share a file/folder with a user/group or as public link.
• Syntax: /shares
• Method: POST
• POST Arguments: path - (string) path to the file/folder which should be shared
• POST Arguments: shareType - (int) ‘0’ = user; ‘1’ = group; ‘3’ = public link
• POST Arguments: shareWith - (string) user / group id with which the file should be shared
• POST Arguments: publicUpload - (boolean) allow public upload to a public shared folder (true/false)
• POST Arguments: password - (string) password to protect public link Share with
• POST Arguments: permissions - (int) 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default:
31, for public shares: 1)
• Mandatory fields: shareType, path and shareWith for shareType 0 or 1.
• Result: XML containing the share ID (int) of the newly created share
Statuscodes:
• 100 - successful
• 400 - unknown share type
• 403 - public upload was disabled by the admin
• 404 - file couldn’t be shared
4.10.5 Delete Share
Remove the given share.
• Syntax: /shares/<share_id>
• Method: DELETE
• Arguments: share_id - (int) share ID
Statuscodes:
• 100 - successful
• 404 - file couldn’t be deleted
4.10. OCS Share API
139
ownCloud Developer Manual, Release 6.0
4.10.6 Update Share
Update a given share. Only one value can be updated per request.
• Syntax: /shares/<share_id>
• Method: PUT
• Arguments: share_id - (int) share ID
• PUT Arguments: permissions - (int) update permissions (see “Create share” above)
• PUT Arguments: password - (string) updated password for public link Share
• PUT Arguments: publicUpload - (boolean) enable (true) /disable (false) public upload for public shares
Note that only one of “password” or “publicUpload” can be specified at once.
Statuscodes:
• 100 - successful
• 400 - wrong or no update parameter given
• 403 - public upload disabled by the admin
• 404 - couldn’t update share
4.11 Intro
Please make sure you have set up a development environment:
• Development Environment
4.12 Core related docs
• Translation
• Unit-Testing
• Theming ownCloud
• App config
• OCS Share API
140
Chapter 4. Core Development
CHAPTER
FIVE
OWNCLOUD TEST PILOTS
The ownCloud Test Pilots take care of helping to test and improve different server setups with ownCloud.
5.1 Why do you want to join
There are many different setups and people have different interests. If you want ownCloud to run well on NginX for
instance someone has to test this configuration.
Furthermore during bugfixing the ownCloud developers often do not have the possibility to reproduce the bug in a
given environment nor they are able confirm that it was fixed. As a member of the Test Pilot Team you could act as a
contact person for a specific area to help developers fix the bugs you care about.
Another benefit is a more efficient relationship to the developers: You know what people are responsible for which
parts and it is easier to get help.
If you want you will be listed as an active contributor on the owncloud.org page.
5.2 Who can join
Anyone who is interested in improving the quality on his/her setup and is willing to communicate with developers and
other testers.
5.3 How do you join
Simply register on the testpilot mailing list and send an introduction of your personal setup and interests to [email protected]
You can also join the #owncloud-testing channel on irc.freenode.net but keep in mind that we may take longer to
answer ;)
For further questions or help you can also send a mail to:
• [email protected] (IRC: dragotin)
• [email protected] (IRC: Raydiation)
141
ownCloud Developer Manual, Release 6.0
5.4 What do you do
You will receive mails from the mailinglist and also from the bugtracker if developers need your help. Also there
will be announcements of new releases and preview releases on the mailing list which give you the possibility to test
releases early on and help developers to fix them.
We are looking forward to working with you :)
142
Chapter 5. ownCloud Test Pilots
CHAPTER
SIX
BUGTRACKER
6.1 Code Reviews on GitHub
Given enough eyeballs, all bugs are shallow
—Linus’ Law
6.1.1 Introduction
In order to increase the code quality within ownCloud, developers are requested to perform code reviews. As we are
now heavily using the GitHub platform these code review shall take place on GitHub as well.
6.1.2 Precondition
From now on no direct commits/pushes to master or any of the stable branches are allowed in general. Every code
change - even one liners - have to be reviewed!
6.1.3 How will it work?
1. A developer will submit his changes on GitHub via a pull request. GitHub:help - using pull requests #. Within
the pull request the developer could already name other developers (using @GitHubusername) and ask them for
review.
2. Other developers (either named or at free will) have a look at the changes and are welcome to write comments
within the comment field. #. In case the reviewer is okay with the changes and thinks all his comments and
suggestions have been take into account a :+1 on the comment will signal a positive review.
3. Before a pull request will be merged into master or the corresponding branch at least 2 reviewers need to give
:+1 score.
4. Our continuous integration server will give an additional indicator for the quality of the pull rquest.
6.1.4 Examples
These are two examples that are considered to be good examples of how pull requests should be handled
• https://github.com/owncloud/core/pull/121
• https://github.com/owncloud/core/pull/146
143
ownCloud Developer Manual, Release 6.0
6.1.5 Questions?
Feel free to drop a line on the mailing list or join us on IRC.
6.2 Kanban Board
This chapter contains a lot of information about the development process the ownCloud community tries to follow,
so please take your time to digest all the information. In any case remember this page as the documentation on how
it should be done. Nothing here is set in stone, so if you think something should be changed please discuss it on the
mailing list.
6.2.1 Kanban Board = github issues + huboard
We are using http://huboard.com to visualize ownCloud github issues as a kanban board (see: core, apps, mirall):
As you may have noticed, the columns of the kanban board represent the life-cycle of an issue (be it a Bug or an
Enhancement). An issue flows from the 1 - Backlog on the left to the 7 - To release column on the right and is not
closed until it has been released. Instead we pull an issue to the next column by changing the label.
6.2.2 The Labels
The following list shows what the labels mean in the life-cycle and will hopefully help you decide how to label an
issue.
Backlog
Why do we have it? To keep us focused on finishing issues that we started, new issues will be hidden in this column.
In huboard you can see the list of things that we could think about by clicking the small arrow in the top left
corner of the concept column header.
What does a developer think? “Maybe later.”
When can I pull? Since this is the bucket for whatever might be done you should only pick issues from the backlog
when there is no other issue that you can work on. It is more important to finish an issue currently on the Kanban
board than to pull a new one into the flow because only released issues have a value to our users!
144
Chapter 6. Bugtracker
ownCloud Developer Manual, Release 6.0
Who is Assigned? Either a maintainer feels directly responsible for the issue and assigns himself or the gatekeeper
(the guys having a look at unassigned bugs) will try to determine the responsible developer.
Concept
Why do we have it? Our think before you act phase serves two purposes. A Bug is in the concept phase while we are
trying to figure out why something is broken (analysis). An Enhancement is in the concept phase until we have
decided how to implement it (design).
What does a developer think? “I’ll write a Scenario for our BDD in Gherkin and post it as a comment. I can always
look at the existing ones to get an inspiration how to phrase them as “Given . . . when . . . then . . . ““
When can I pull? As long as you think and discuss on how to implement an enhancement or how to solve a bug you
should leave the concept label assigned. Two things should be documented in a comment to the issue before
moving it to the “To develop” step:
• At least one Scenario – written in Gherkin – that tells you and the tester when the issue is ready to be
released.
• A concept describing the planned implementation. This can be as simple as a “this just needs changes to
the login screen css” or so complex that you link to a blog entry somewhere else.
Who is Assigned? The maintainer that feels responsible for the issue.
To Develop
Why do we have it? Now that we have a plan, any developer can pick an issue from this column and start implementing it. If the issue is also marked with Junior Job this might be a good starting point for new developers.
What does a developer think? “Nice! I can safely implement it that way because more than one person has put his
brain to the task of coming up with a good solution. Here! Me! I’ll do it!”
When can I pull? If you feel like diving into the code and getting your hands dirty you should look for issues with
this label. In the comments, there should be a gherkin scenario to tell you when you are done and a concept
describing how to implement it. Before you start move the issue to the “Developing” step by assigning the “4 –
Developing” label.
Who is Assigned? No one. Especially not if you are working on something else!
Developing
Why do we have it? This is where the magic happens. If it’s a Bug the fix will be submitted as a PULL REQUEST
to the master or corresponding stable branch. If its an Enhancement code will be committed to a feature branch.
What does a developer think? “You know, I’m at it. By the way, I’ll also write unit tests. When I’m done I’ll push
the issue with a commit containing “push GH-#” where # is the issue number. If I have an idea of who should
review it I can also notify them with @githubusername”
When can I pull? As long as you are writing code for the issue or if any unit test fails you should leave the “4 –
Developing” label assigned. Two things should have been implemented before moving the issue to the “To
review” step:
• The enhancement or bug in question
• Unit tests for the changed and added code.
Who is Assigned? The most active developer should assign himself.
6.2. Kanban Board
145
ownCloud Developer Manual, Release 6.0
To Review
Why do we have it? Instead of directly committing to master we agree that a second set of eyes will spot bugs
and increase our code quality and give us an opportunity to learn from each other. See also our Code Review
Documentation
What does a developer think? “I’ll check the Scenario described earlier works as expected. If necessary I’ll update
the related Gherkin Scenarios. Jenkins will test the scenario on all kinds of platforms, webserver and database
combinations with cucumber.”
When can I pull? If you feel like making sure an issue works as expected you should look for issues with this label.
In the comments you should find a gherkin scenario that can be used as a checklist for what to try. Before you
start move the issue to the “Reviewing” step by assigning the “6 – Reviewing” label.
Who is Assigned? No one. Especially not if you are working on something else!
Reviewing
Why do we have it? With the Gherkin Scenario from the Concept Phase reviewers have a checklist to test if a Bug
has been solved and if an Enhancement works as expected. The most eager reviewer we have is Jenkins.
When it comes to testing he soldiers on going through the different combinations of platform, webserver and
database.
What does a developer think? “Damn! If I had written the Gherkin Scenarios and Cucumber Step Definitions I
could leave the task of testing this on the different combinations of platform, webserver and database to Jenkins.
I’ll miss something when doing this manually.*
When can I pull? As long as you are reviewing the issue the you should leave the “6 – Reviewing” label assigned.
Before moving the issue to the “To review” step the issue should have been resolved, meaning that not only the
issue has been implemented but also no other functionality has been broken.
Who is Assigned? The most active reviewer should assign himself.
To Release
Why do we have it? This is a list of issues that will make it into the next release. It serves as a source for the
changelog, as well as a reminder of the work we can already be proud of.
What does a developer think? “Look at all the shiny things we will release with the next version of ownCloud!”
When can I pull? This is the last step of the Kanban board. When the Release finally happens the issue will be closed
and removed from the board.
Who is Assigned? No one.
While we stated before that said that we push issues to the next column, we can of course move the item back and
forth arbitrarily. Basically you can drag the issue around in the huboard or just change the label when viewing the
issue in the GitHub.
6.2.3 Reviewing considered impossible?
How can you possibly review an issue when it requires you to test various combinations of browsers, platforms,
databases and maybe even app combinations? Well, you can’t. But you can write a gherkin scenario that can be used
to write an automated test that is executed by Jenkins on every commit to the main repositories. If for some reason
Jenkins cannot be used for the review you will find yourself in the very uncomfortable situation where you release half
tested code that will hopefully not eat user data. Seriously! Write gherkin scenarios!
146
Chapter 6. Bugtracker
ownCloud Developer Manual, Release 6.0
6.2.4 Other Labels
Priority Labels
• Panic should be used with caution. It is reserved for Bugs that would result in the loss of files or other user data.
An Enhancement marked as Panic is expected by ownCloud users for the next release. In either case an open
Panic issue will prevent a release.
• Attention is not as hard as Panic. But we really want this in the next release and will dedicate more effort for it.
But if we think the issue is not ready for the next release we will postpone it to the next one.
• Regression is something that worked in a previous release but is now not working as expected or missing. If a
certain functionality is up for code refactoring, the developer should describe all possible use cases as a Gherkin
scenarios beforehand, so that any scenarios that isn’t implemented before the required milestone can be marked
as a regression. If a regression is found after a release, the reporter – or the developer triaging the issue – should
describe the functionality as a Gherkin scenario and either fix it or assign it to the developer in charge of that
part.
App Labels
In the apps repository there are labels like app:gallery and app:calendar. The app: prefix is used to allow
developers to filter issues related to a specific app.
Resolution Status
• Fixed – Should be assigned to issues in to Release
• Won’t fix – Reason is given as a comment
• Duplicate – Corresponding bug is given in a comment (using #guthubissuenumber)
Misc Labels
• Needs info – Either from a developer or the bug reporter. This is nearly as severe as Panic, because no further
action can be taken
• L18n – A translation issue go see our transifex
• Junior Job – The issue is considered a good starting point to get involved in ownCloud development
6.2.5 Milestones equal Releases
Releases are planned via milestones which contain all the Enhancements and Bugs that we plan to release when the
Deadline is met. When the Deadline approaches we will push new Enhancement request and less important bugs to
the next milestone. This way a milestone will upon release contain all the issues that make up the changelog for the
release. Furthermore, huboard allows us to filter the Kanban board by Milestone, making it especially easy to focus
on the current Release.
Thank you for helping ownCloud by reporting bugs. Before submitting an issue, please read Issue submission guidelines first.
Bugs related to the core should be entered into the ownCloud Core Repository Issues. If your report is related to an
application, their bugtrackers will be also found in the same guideline page.
6.2. Kanban Board
147
ownCloud Developer Manual, Release 6.0
148
Chapter 6. Bugtracker
CHAPTER
SEVEN
HELP AND COMMUNICATION
7.1 Mailing lists
Communicate via mail on our mailing lists.
7.2 IRC channels
Chat with us on IRC. All channels are on irc.freenode.net
• Setup: #owncloud
• Testing: #owncloud-testing
• Development: #owncloud-dev
• Design: #owncloud-design
7.3 Maintainers
• Contact a maintainer of a certain app or division
149
ownCloud Developer Manual, Release 6.0
150
Chapter 7. Help and Communication
CHAPTER
EIGHT
ANDROID APPLICATION
DEVELOPMENT
This document will describe how to the use ownCloud Android Library. The ownCloud Android Library allows a
developer to communicate with any ownCloud server; among the features included are file synchronization, upload
and download of files, delete rename files and folders, etc.
This library may be added to a project and seamlessly integrates any application with ownCloud.
The tool needed is any IDE for Android. This guide includes some screenshots showing examples in Eclipse.
8.1 Library Installation
8.1.1 Obtaining the library
The ownCloud Android library may be obtained from the following Github repository:
https://github.com/owncloud/android-library
Once obtained, this code should be compiled. The Github repository not only contains the library, but also a sample
project, sample_client sample_client properties/android/librerias , which will assist in learning how to use the library.
8.1.2 Add the library to a project
There are different methods to add an external library to a project, then we will describe one of them.
1. Compile the ownCloud Android Library
2. Define a dependency within your project.
For that, access to Properties > Android > Library ** ** and click on add and select the ownCloud Android library
151
ownCloud Developer Manual, Release 6.0
Then all the public classes and methods of the library will be available for your own app.
8.2 Examples
8.2.1 Init the library
Start using the library; it is needed to init the object mClient that will be in charge of keeping the communication with
the server.
152
Chapter 8. Android Application Development
ownCloud Developer Manual, Release 6.0
Code example
public class MainActivity extends Activity
implements OnRemoteOperationListener,
OnDatatransferProgressListener {
private OwnCloudClient mClient;
private Handler mHandler = new Handler();
...
public void onCreate(Bundle savedInstanceState) {
...
// Parse URI to the base URL of the ownCloud server
Uri serverUri = Uri.parse(getString(R.string.server_base_url));
// Create client object to perform remote operations
mClient = OwnCloudClientFactory.createOwnCloudClient(
serverUri,
this,
// Activity or Service context
true);
8.2.2 Set credentials
Authentication on the app is possible by 3 different methods:
• Basic authentication, user name and password
• Bearer access token (oAuth2)
• Cookie (SAML-based single-sign-on)
Code example
package com.owncloud.android.lib.common;
public class OwnCloudClient extends HttpClient {
...
// Set basic credentials
client.setCredentials(
OwnCloudCredentialsFactory.newBasicCredentials(username, password)
);
// Set bearer access token
client.setCredentials(
OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
);
// Set SAML2 session token
client.setCredentials(
OwnCloudCredentialsFactory.newSamlSsoCredentials(cookie)
);
}
8.2. Examples
153
ownCloud Developer Manual, Release 6.0
8.2.3 Create a folder
Create a new folder on the cloud server, the info needed to be sent is the path of the new folder.
Code example
private void startFolderCreation(String newFolderPath) {
CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(newFolderPath, false)
createOperation.execute( mClient , this , mHandler);
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof CreateRemoteFolderOperation) {
if (result.isSuccess()) {
// do your stuff here
}
}
...
}
8.2.4 Read folder
Get the content of an existing folder on the cloud server, the info needed to be sent is the path of the folder, in the
example shown it has been asked the content of the root folder. As answer of this method, it will be received an array
with all the files and folders stored in the selected folder.
Code example
private void startReadRootFolder() {
ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(FileUtils.PATH_SEPARATOR
// root folder
refreshOperation.execute(mClient, this, mHandler);
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof ReadRemoteFolderOperation) {
if (result.isSuccess()) {
List< RemoteFile > files = result.getData();
// do your stuff here
}
}
...
}
8.2.5 Read file
Get information related to a certain file or folder, information obtained is: filePath, filename, isDirectory,
size and date.
154
Chapter 8. Android Application Development
ownCloud Developer Manual, Release 6.0
Code example
private void startReadFileProperties(String filePath) {
ReadRemoteFileOperation readOperation = new ReadRemoteFileOperation(filePath);
readOperation.execute(mClient, this, mHandler);
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof ReadRemoteFileOperation) {
if (result.isSuccess()) {
RemoteFile file = result.getData()[0];
// do your stuff here
}
}
...
}
8.2.6 Delete file or folder
Delete a file or folder on the cloud server. The info needed is the path of folder/file to be deleted.
Code example
private void startRemoveFile(String filePath) {
RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath);
removeOperation.execute( mClient , this , mHandler);
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof RemoveRemoteFileOperation) {
if (result.isSuccess()) {
// do your stuff here
}
}
...
}
8.2.7 Download a file
Download an existing file on the cloud server. The info needed is path of the file on the server and targetDirectory,
path where the file will be stored on the device.
Code example
private void startDownload(String filePath, File targetDirectory) {
DownloadRemoteFileOperation downloadOperation = new DownloadRemoteFileOperation(filePath, targetDir
downloadOperation.addDatatransferProgressListener(this);
downloadOperation.execute( mClient, this, mHandler);
}
8.2. Examples
155
ownCloud Developer Manual, Release 6.0
@Override
public void onRemoteOperationFinish( RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof DownloadRemoteFileOperation) {
if (result.isSuccess()) {
// do your stuff here
}
}
}
@Override
public void onTransferProgress( long progressRate, long totalTransferredSoFar, long totalToTransfer,
mHandler.post( new Runnable() {
@Override
public void run() {
// do your UI updates about progress here
}
});
}
8.2.8 Upload a file
Upload a new file to the cloud server. The info needed is fileToUpload, path where the file is stored on the device,
remotePath, path where the file will be stored on the server and mimeType.
Code example
private void startUpload (File fileToUpload, String remotePath, String mimeType) {
UploadRemoteFileOperation uploadOperation = new UploadRemoteFileOperation( fileToUpload.getAbsolute
uploadOperation.addDatatransferProgressListener(this);
uploadOperation.execute(mClient, this, mHandler);
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof UploadRemoteFileOperation) {
if (result.isSuccess()) {
// do your stuff here
}
}
}
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, S
mHandler.post( new Runnable() {
@Override
public void run() {
// do your UI updates about progress here
}
});
}
156
Chapter 8. Android Application Development
ownCloud Developer Manual, Release 6.0
8.2.9 Move a file or folder
Move an exisintg file or folder to a different location in the ownCloud server. Parameters needed are the path to the
file or folder to move, and the new path desired for it. The parent folder of the new path must exist in the server.
When the parameter ‘overwrite’ is set to ‘true’, the file or folder is moved even if the new path is already used by a
different file or folder. This one will be replaced by the former.
Code example
private void startFileMove(String filePath, String newFilePath, boolean overwrite) {
MoveRemoteFileOperation moveOperation = new MoveRemoteFileOperation(filePath, newFilePath, overwrit
moveOperation.execute( mClient , this , mHandler);
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof MoveRemoteFileOperation) {
if (result.isSuccess()) {
// do your stuff here
}
}
...
}
8.2.10 Read shared items by link
Get information about what files and folder are shared by link (the object mClient contains the information about the
server url and account)
Code example
private void startAllSharesRetrieval() {
GetRemoteSharesOperation getSharesOp = new GetRemoteSharesOperation();
getSharesOp.execute( mClient , this , mHandler);
}
@Override
public void onRemoteOperationFinish( RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof GetRemoteSharesOperation) {
if (result.isSuccess()) {
ArrayList< OCShare > shares = new ArrayList< OCShare >();
for (Object obj: result.getData()) {
shares.add(( OCShare) obj);
}
// do your stuff here
}
}
}
8.2. Examples
157
ownCloud Developer Manual, Release 6.0
8.2.11 Get the share resources for a given file or folder
Get information about what files and folder are shared by link on a certain folder. The info needed is filePath, path of
the file/folder on the server, the Boolean variable, getReshares, come from the Sharing api, from the moment it is not
in use within the ownCloud Android library.
Code example
private void startSharesRetrievalForFileOrFolder(String filePath, boolean getReshares) {
GeteRemoteSharesForFileOperation operation = new GetRemoteSharesForFileOperation(filePath, getResha
operation.execute( mClient, this, mHandler);
}
private void startSharesRetrievalForFilesInFolder(String folderPath, boolean getReshares) {
GetRemoteSharesForFileOperation operation = new GetRemoteSharesForFileOperation(folderPath, getResh
operation.execute( mClient, this, mHandler);
}
@Override
public void onRemoteOperationFinish( RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof GetRemoteSharesForFileOperation) {
if (result.isSuccess()) {
ArrayList< OCShare > shares = new ArrayList< OCShare >();
for (Object obj: result.getData()) {
shares.add(( OCShare) obj);
}
// do your stuff here
}
}
}
8.2.12 Share link of file or folder
Share a file or a folder from your cloud server by link.
The info needed is filePath, the path of the item that you want to share and Password, this comes from the Sharing api,
from the moment it is not in use within the ownCloud Android library.
Code example
private void startCreationOfPublicShareForFile(String filePath, String password) {
CreateRemoteShareOperation operation = new CreateRemoteShareOperation(filePath, ShareType.PUBLIC_LI
operation.execute( mClient , this , mHandler);
}
private void startCreationOfGroupShareForFile(String filePath, String groupId) {
CreateRemoteShareOperation operation = new CreateRemoteShareOperation(filePath, ShareType.GROUP, gr
operation.execute(mClient, this, mHandler);
}
private void startCreationOfUserShareForFile(String filePath, String userId) {
CreateRemoteShareOperation operation = new CreateRemoteShareOperation(filePath, ShareType.USER, use
operation.execute(mClient, this, mHandler);
}
158
Chapter 8. Android Application Development
ownCloud Developer Manual, Release 6.0
@Override
public void onRemoteOperationFinish( RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof CreateRemoteShareOperation) {
if (result.isSuccess()) {
OCShare share = (OCShare) result.getData ().get(0);
// do your stuff here
}
}
}
8.2.13 Delete a share resource
Stop sharing by link a file or a folder from your cloud server.
The info needed is the object OCShare that you want to stop sharing by link.
Code example
private void startShareRemoval(OCShare share) {
RemoveRemoteShareOperation operation = new RemoveRemoteShareOperation((int) share.getIdRemoteShared
operation.execute( mClient, this, mHandler);
}
@Override
public void onRemoteOperationFinish( RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof RemoveRemoteShareOperation) {
if (result.isSuccess()) {
// do your stuff here
}
}
}
8.2.14 Tips
• Credentials must be set before calling any method
• Paths must not be on URL Encoding
• Correct path: http://www.myowncloudserver.com/owncloud/remote.php/webdav/PopMusic
• Wrong path: http://www.myowncloudserver.com/owncloud/remote.php/webdav/Pop%20Music/
• There are some forbidden characters to be used in folder and files names on the server, same on the ownCloud
Android Library “”,”/”,”<”,”>”,”:”,”“”,”|”,”?”,”*”
• Upload and download actions may be cancelled thanks to the objects uploadOperation.cancel(), downloadOperation.cancel()
• Unit tests, before launching unit tests you have to enter your account information (server url, user and password)
on TestActivity.java
8.2. Examples
159
ownCloud Developer Manual, Release 6.0
160
Chapter 8. Android Application Development
CHAPTER
NINE
IOS APPLICATION DEVELOPMENT
This document will describe how to the use ownCloud iOS library. The ownCloud iOS library for iOS allows a
developer to communicate with any ownCloud server; among the features included are file synchronization, upload
and download of files, delete rename and move of files and folders and share files or folders by link among others.
This library may be added to a project and seamlessly integrates any application with ownCloud.
The tool needed is Xcode 5, this guide includes some screenshots showing examples in Xcode 5.
9.1 Library Installation
9.1.1 Obtaining the library
The ownCloud iOS library may be obtained from the following Github repository:
[email protected]:owncloud/ios-library.git
Once obtained, this code should be compiled with Xcode 5. The Github repository not only contains the library,
ownCloud iOS library, but also contains a sample project, OCLibraryExample, which will assist in learning how to
use the library.
9.1.2 Add the library to a project
There are two methods to add this library to a project.
• Reference the headers and library binary file (.a) directly.
• Include the library as a subproject.
Which method to choose depends on user preference as well as whether the source code and project file of the static
library are available.
Reference headers and library binary files
Follow these steps if this is the desired method.
1. Compile the ownCloud iOS library and run the project. A libownCloudiOS.a file will be generated.
The following files are required:
Library file
• libownCloudiOS.a (Library)
161
ownCloud Developer Manual, Release 6.0
Library Classes
• OCCommunication.h (Accessors) Import in the communication class
• OCErrorMsg.h (Error Messages) Import in the communication class
• OCFileDto.h and OCFileDto.m (File/Folder object) Import when using
• readFolder and readFile methods
• OCFrameworkConstants.h (Customize constants)
2. Add the library file to the project. From the “Build Phases” tab, scroll to “Link binary files” and select the ‘+’ to
add a library. Select the library file.
3. Add the path of the library header files. Under the “Build Settings” tab, select the target library and add the path in
the “Header Search Paths” field.
162
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
4. Remaining in the “Build Setting” tab, add the flag -Obj-C under the “Other Linker Flags” option.
At this stage, the library is included on your project and you can start communicating with the ownCloud server.
9.1. Library Installation
163
ownCloud Developer Manual, Release 6.0
Include the library as a subproject
Follow these steps if this is the desired method.
5. Add the file ownCloud iOS library.xcodeproj to the project via drag and drop.
6. Within the project, navigate to the “Build Phases” tab. Under the “Target Dependencies” section, select the ‘+’ and
choose the library target.
164
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
7. Link the library file to the project target. Under the “Build Phases” tab, select the ‘+’ under the “Link Binary with
Libraries” section and select the library file.
9.1. Library Installation
165
ownCloud Developer Manual, Release 6.0
8. Add the flag -Obj-C to “Other Linker Flags” under the project target on the “Build Settings” tab.
9. Finally add the path of the library headers. Under the “Build Settings” tab, add the path under the “Header Search
Paths” option.
9.1.3 Sources
• Creating a static library in iOS tutorial (raywenderlich.com)
166
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
• Apple iOS static library documentation
9.2 Examples
9.2.1 Init the library
Start using the library, it is needed to init the object OCCommunication.
We recommend using the singleton method in the AppDelegate class in order to use the ownCloud iOS library.
Code example
#import "OCCommunication.h"
+ (OCCommunication *)sharedOCCommunication
{
static OCCommunication* sharedOCCommunication = nil;
if (sharedOCCommunication == nil)
{
sharedOCCommunication = [ [ OCCommunicationalloc] init ];
}
return sharedOCCommunication;
}
Also could happen that you need to overwrite the class AFURLSessionManager to manage SSL Certificates
#import "OCCommunication.h"
+ (OCCommunication*)sharedOCCommunication
{
static OCCommunication* sharedOCCommunication = nil;
if (sharedOCCommunication == nil)
{
//Network Upload queue for NSURLSession (iOS 7)
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurat
configuration.HTTPMaximumConnectionsPerHost = 1;
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
OCURLSessionManager *uploadSessionManager = [[OCURLSessionManager alloc] initWithSessionConfigura
[uploadSessionManager.operationQueue setMaxConcurrentOperationCount:1];
[uploadSessionManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallenge
return NSURLSessionAuthChallengePerformDefaultHandling;
}];
sharedOCCommunication = [[OCCommunication alloc] initWithUploadSessionManager:uploadSessionManage
}
return sharedOCCommunication;
}
9.2.2 Set credentials
Authentication on the app is possible by 3 different methods:
9.2. Examples
167
ownCloud Developer Manual, Release 6.0
• Basic authentication, user name and password
• Cookie
• Token (oAuth)
Code example
#Basic authentication, user name and password
[[ AppDelegate sharedOCCommunication ] setCredentialsWithUser : userName andPassword : password ];
#Authentication with cookie
[[ AppDelegate sharedOCCommunication ] setCredentialsWithCookie : cookie ];
#Authentication with token
[[ AppDelegate sharedOCCommunication ] setCredentialsOauthWithToken : token ];
9.2.3 Create a folder
Create a new folder on the cloud server, the info needed to be sent is the path of the new folder.
Code example
[[ AppDelegate sharedOCCommunication ] createFolder :path onCommunication : [ AppDelegate sharedOCCom
successRequest :^( NSHTTPURLResponse *response, NSString *redirectedServer) {
//Folder Created
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error) {
//Failure
switch (response.statusCode) {
case kOCErrorServerUnauthorized :
//Bad credentials
break;
case kOCErrorServerForbidden :
//Forbidden
break;
case kOCErrorServerPathNotFound :
//Not Found
break;
case kOCErrorServerTimeout :
//timeout
break;
default:
//default
break;
}
}
errorBeforeRequest :^( NSError *error) {
//Error before request
168
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
if (error.code == OCErrorForbidenCharacters) {
//Forbidden characters
}
else
{
//Other error
}
}];
9.2.4 Read folder
Get the content of an existing folder on the cloud server, the info needed to be sent is the path of the folder. As answer
of this method, it will be received an array with all the files and folders stored in the selected folder.
Code example
[[ AppDelegate sharedOCCommunication] readFolder:path onCommunication:[ AppDelegate sharedOCCommunica
successRequest:^( NSHTTPURLResponse *response, NSArray *items, NSString *redirectedServer) {
//Success
for ( OCFileDto * ocFileDto in items) {
NSLog( @"item path: %@%@" , ocFileDto.filePath, ocFileDto.fileName);
}
}
failureRequest:^( NSHTTPURLResponse *response, NSError *error) {
//Failure
switch (response.statusCode) {
case kOCErrorServerPathNotFound :
//Path not found
break;
case kOCErrorServerUnauthorized :
//Bad credentials
break;
case kOCErrorServerForbidden :
//Forbidden
break;
case kOCErrorServerTimeout :
//Timeout
break ;
default :
break;
}
}];
9.2.5 Read file
Get information related to a certain file or folder. Although, more information can be obtained, the library only gets
the eTag.
Other properties of the file or folder may be obtained: filePath, filename, isDirectory, size and date
9.2. Examples
169
ownCloud Developer Manual, Release 6.0
Code example
[[ AppDelegate sharedOCCommunication ] readFile :path onCommunication :[ AppDelegate sharedOCCommunic
successRequest :^( NSHTTPURLResponse *response, NSArray *items, NSString *redirectedServer) {
OCFileDto *ocFileDto = [items objectAtIndex : 0 ];
NSLog ( @"item etag: %lld" , ocFileDto. etag); }
failureRequest :^( NSHTTPURLResponse *response, NSError *error) {
switch (response.statusCode) {
case kOCErrorServerPathNotFound:
//Path not found
break;
case kOCErrorServerUnauthorized:
//Bad credentials
break;
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorServerTimeout:
//Timeout
break;
default:
break;
}
}];
9.2.6 Move file or folder
Move a file or folder from their current path to a new one on the cloud server. The info needed is the origin path and
the destiny path.
Code example
[[ AppDelegate sharedOCCommunication ] moveFileOrFolder :sourcePath toDestiny :destinyPath onCommunic
successRequest :^( NSHTTPURLResponse *response, NSString *redirectedServer) {
//File/Folder moved or renamed
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error) {
//Failure
switch (response.statusCode) {
case kOCErrorServerPathNotFound:
//Path not found
break;
case kOCErrorServerUnauthorized:
//Bad credentials
break;
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorServerTimeout:
//Timeout
break;
default:
170
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
break;
}
}
errorBeforeRequest :^( NSError *error) {
if (error.code == OCErrorMovingTheDestinyAndOriginAreTheSame) {
//The destiny and the origin are the same
}
else if (error.code == OCErrorMovingFolderInsideHimself) {
//Moving folder inside himself
}
else if (error.code == OCErrorMovingDestinyNameHaveForbiddenCharacters) {
//Forbidden Characters
}
else
{
//Default
}
}];
9.2.7 Delete file or folder
Delete a file or folder on the cloud server. The info needed is the path to delete.
Code example
[[ AppDelegate sharedOCCommunication ] deleteFileOrFolder :path onCommunication :[ AppDelegate
sharedOCCommunication ] successRequest :^( NSHTTPURLResponse *response, NSString *redirectedServer) {
//File or Folder deleted
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error) {
switch (response.statusCode) {
case kOCErrorServerPathNotFound:
//Path not found
break;
case kOCErrorServerUnauthorized:
//Bad credentials
break;
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorServerTimeout:
//Timeout
break;
default:
break;
}
}];
9.2. Examples
171
ownCloud Developer Manual, Release 6.0
9.2.8 Download a file
Download an existing file on the cloud server. The info needed is the server URL, path of the file on the server and
localPath, path where the file will be stored on the device and a boolean to indicate if is neccesary to use LIFO queue
or FIFO.
Code example
NSOperation *op = nil;
op = [[ AppDelegate sharedOCCommunication ] downloadFile :remotePath toDestiny :localPath withLIFOSys
progressDownload :^( NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRe
//Calculate percent
float percent = ( float)totalBytesRead / totalBytesExpectedToRead;
NSLog ( @"Percent of download: %f" , percent); }
successRequest :^(NSHTTPURLResponse *response, NSString *redirectedServer) {
//Download complete
}
failureRequest :^(NSHTTPURLResponse *response, NSError *error) {
switch (response. statusCode) {
case kOCErrorServerUnauthorized:
//Bad credentials
break;
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorProxyAuth:
//Proxy access required
break;
case kOCErrorServerPathNotFound:
//Path not found
break;
default:
//Default
break;
}
}
shouldExecuteAsBackgroundTaskWithExpirationHandler :^{
[op cancel ];
}];
9.2.9 Download a file with background session
Download an existing file storaged on the cloud server using background session, only supported by iOS 7 and higher.
The info needed is, the server URL: path where the file is stored on the server; localPath: path where the file will be
stored on the device; and NSProgress: object where get the callbacks of the upload progress.
To get the callbacks of the progress is needed use a KVO in the progress object. We add the code in this example of
the call to set the KVO and the method where catch the notifications.
172
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
Code example
NSURLSessionDownloadTask *downloadTask = nil;
NSProgress *progress = nil;
downloadTask = [_sharedOCCommunication downloadFileSession:serverUrl toDestiny:localPath defaultPrior
//Upload complete
} failureRequest:^(NSURLResponse *response, NSError *error) {
switch (error.code) {
case kCFURLErrorUserCancelledAuthentication:
//Authentication cancelled
break;
default:
switch (response.statusCode) {
case kOCErrorServerUnauthorized :
//Bad credentials
break;
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorProxyAuth:
//Proxy access required
break;
case kOCErrorServerPathNotFound:
//Path not found
break;
default:
//Default
break;
}
break;
}
}];
// Observe fractionCompleted using KVO
[progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew cont
//Method to catch the progress notifications with callbacks
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
{
if ([keyPath isEqualToString:@"fractionCompleted"] && [object isKindOfClass:[NSProgress class]])
NSProgress *progress = (NSProgress *)object;
float percent = roundf (progress.fractionCompleted * 100);
//We make it on the main thread because we came from a delegate
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Progress is %f", percent);
});
}
}
9.2. Examples
173
ownCloud Developer Manual, Release 6.0
9.2.10 Set callback when background download task finishes
Method to set callbacks of the pending download transfers when the app starts. It’s used when there are pendings
download background transfers. The block is executed when a pending background task finishes.
Code example
[[AppDelegate sharedOCCommunication] setDownloadTaskComleteBlock:^NSURL *(NSURLSession *session, NSUR
}];
9.2.11 Set progress callback with pending background download tasks
Method to set progress callbacks of the pending download transfers. It’s used when there are pendings background
download transfers. The block is executed when a pending task get a input porgress.
Code example
[[AppDelegate sharedOCCommunication] setDownloadTaskDidGetBodyDataBlock:^(NSURLSession *session, NSUR
}];
9.2.12 Upload a file
Upload a new file to the cloud server. The info needed is localPath, path where the file is stored on the device and
server URL, path where the file will be stored on the server.
Code example
NSOperation *op = nil;
op = [[ AppDelegate sharedOCCommunication ] uploadFile :localPath toDestiny : remotePath onCommunicat
progressUpload :^( NSUInteger bytesWrote, long long totalBytesWrote, long long totalBytesExpectedToWr
//Calculate upload percent
if ( totalBytesExpectedToRead/1024 != 0) {
if ( bytesWrote > 0) {
float percent = totalBytesWrote* 100 / totalBytesExpectedToRead;
NSLog ( @"Percent: %f" , percent);
}
}
}
successRequest :^( NSHTTPURLResponse *response, NSString *redirectedServer) {
//Upload complete
}
failureRequest :^( NSHTTPURLResponse *response, NSString *redirectedServer, NSError *error) {
switch (response. statusCode) {
case kOCErrorServerUnauthorized :
//Bad credentials
break;
174
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorProxyAuth:
//Proxy access required
break;
case kOCErrorServerPathNotFound:
//Path not found
break;
default:
//Default
break;
}
}
failureBeforeRequest :^( NSError *error) {
switch (error.code) {
case OCErrorFileToUploadDoesNotExist:
//File does not exist
break;
default:
//Default
break;
}
}
shouldExecuteAsBackgroundTaskWithExpirationHandler :^{
[op cancel];
}];
9.2.13 Upload a file with background session
Upload a new file to the cloud server using background session, only supported by iOS 7 and higher.
The info needed is localPath, path where the file is stored on the device and server URL, path where the file will be
stored on the server and NSProgress object where get the callbacks of the upload progress.
To get the callbacks of the progress is needed use a KVO in the progress object. We add the code in this example of
the call to set the KVO and the method where catch the notifications.
Code example
NSURLSessionUploadTask *uploadTask = nil;
NSProgress *progress = nil;
uploadTask = [[AppDelegate sharedOCCommunication] uploadFileSession:localPath toDestiny:remotePath on
//Upload complete
} failureRequest:^(NSURLResponse *response, NSString *redirectedServer, NSError *error) {
switch (response.statusCode) {
case kOCErrorServerUnauthorized :
//Bad credentials
break;
case kOCErrorServerForbidden:
//Forbidden
break;
case kOCErrorProxyAuth:
//Proxy access required
9.2. Examples
175
ownCloud Developer Manual, Release 6.0
break;
case kOCErrorServerPathNotFound:
//Path not found
break;
default:
//Default
break;
}
}];
// Observe fractionCompleted using KVO
[progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew cont
//Method to catch the progress notifications with callbacks
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
{
if ([keyPath isEqualToString:@"fractionCompleted"] && [object isKindOfClass:[NSProgress class]])
NSProgress *progress = (NSProgress *)object;
float percent = roundf (progress.fractionCompleted * 100);
//We make it on the main thread because we came from a delegate
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Progress is %f", percent);
});
}
}
9.2.14 Set callback when background task finish
Method to set callbacks of the pending transfers when the app starts. It’s used when there are pendings background
transfers. The block is executed when a pending background task finished.
Code example
[[AppDelegate sharedOCCommunication] setTaskDidCompleteBlock:^(NSURLSession *session, NSURLSessionTas
}];
9.2.15 Set progress callback with pending background tasks
Method to set progress callbacks of the pending transfers. It’s used when there are pendings background transfers.
The block is executed when a pending task get a input porgress.
176
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
Code example
[[AppDelegate sharedOCCommunication] setTaskDidSendBodyDataBlock:^(NSURLSession *session, NSURLSessio
}];
9.2.16 Check if the server supports Sharing api
The Sharing API is included in ownCloud 5.0.13 and greater versions. The info needed is activeUser.url, the server
URL that you want to check.
Code Example
[[ AppDelegate sharedOCCommunication ] hasServerShareSupport :_activeUser.url onCommunication :[ AppD
successRequest :^( NSHTTPURLResponse *response, BOOL hasSupport, NSString *redirectedServer) {
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error){
}
}];
9.2.17 Read shared all items by link
Get information about what files and folder are shared by link.
The info needed is Path, the server URL that you want to check.
Code example
[[ AppDelegate sharedOCCommunication ] readSharedByServer :path onCommunication :[ AppDelegate shared
successRequest :^( NSHTTPURLResponse *response, NSArray *items, NSString *redirectedServer) {
NSLog ( @"Item: %d" , items);
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error){
NSLog ( @"error: %@" , error);
NSLog ( @"Operation error: %d" , response.statusCode);
}];
9.2.18 Read shared items by link of a path
Get information about what files and folder are shared by link in a specific path.
The info needed is the server URL that you want to check and the specific path tha you want to check.
9.2. Examples
177
ownCloud Developer Manual, Release 6.0
Code example
[[AppDelegate sharedOCCommunication] readSharedByServer:serverPath andPath:path onCommunication:[AppD
NSLog ( @"Item: %d" , items);
} failureRequest:^(NSHTTPURLResponse *response, NSError *error) {
NSLog ( @"error: %@" , error);
NSLog ( @"Operation error: %d" , response.statusCode);
}];
9.2.19 Share link of file or folder
Share a file or a folder from your cloud server by link. The info needed is Path, your server URL and the path of the
item that you want to share (for example /folder/file.pdf)
Code example
[[ AppDelegate sharedOCCommunication ] shareFileOrFolderByServer :path andFileOrFolderPath :itemPath
successRequest :^( NSHTTPURLResponse *response, NSString *token, NSString *redirectedServer) {
NSString *sharedLink = [ NSString stringWithFormat:@ ‘path/public.php?service=files&t=%@ <mailto:path
, token];
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error){
[ _delegate endLoading ];
DLog ( @”error.code: %d” , error. code);
DLog (@”server.error: %d”, response. statusCode);
int code = response. statusCode ;
if (error.code == kOCErrorServerPathNotFound) {
}
switch (code) {
case kOCErrorServerPathNotFound:
//File to share not exists
break;
case kOCErrorServerUnauthorized:
//Error login
break;
case kOCErrorServerForbidden:
//Permission error
break;
case kOCErrorServerTimeout:
//Not possible to connect to server
break;
default:
if (error.code == kOCErrorServerPathNotFound) {
//File to share not exists
} else {
//Not possible to connect to the server
}
break;
178
Chapter 9. iOS Application Development
ownCloud Developer Manual, Release 6.0
}
}];
}
NSLog ( @"error: %@" , error);
NSLog ( @"Operation error: %d" , response.statusCode);
}];
9.2.20 Unshare a folder or file by link
Stop sharing by link a file or a folder from your cloud server.
The info needed is Path, your server URL and the Id of the item that you want to Unshare.
Before unsharing an item, you have to read the shared items on the selected server, using the method “ readSharedByServer ” so that you get the array “items” with all the shared elements. These are objects OCShareDto, one of their
properties is idRemoteShared, parameter needed to unshared an element.
Code example
[[ AppDelegate sharedOCCommunication ] unShareFileOrFolderByServer :path andIdRemoteSharedShared :sha
successRequest :^( NSHTTPURLResponse *response, NSString *redirectedServer) {
//File unshared
}
failureRequest :^( NSHTTPURLResponse *response, NSError *error){
//Error
}
];
9.2.21 Check if file of folder is shared
Check if a specific file or folder is shared in your cloud server.
Teh info need is Path, your server URL and the Id of the item that you want.
Before check an item, you have to read the shared items on the selected server, using the method “ readSharedByServer
” so that you get the array “items” with all the shared elements. These are objects OCShareDto, one of their properties
is idRemoteShared, parameter needed to unshared an element.
Code example
[[AppDelegate sharedOCCommunication] isShareFileOrFolderByServer:path andIdRemoteShared:_shareDto.idR
//File/Folder is shared
} failureRequest:^(NSHTTPURLResponse *response, NSError *error) {
//File/Folder is not shared
}];
9.2. Examples
179
ownCloud Developer Manual, Release 6.0
9.2.22 Tips
• Credentials must be set before calling any method
• Paths must not be on URL Encoding
• Correct path: http://www.myowncloudserver.com/owncloud/remote.php/webdav/Pop_Music/
• Wrong path: http://www.myowncloudserver.com/owncloud/remote.php/webdav/Pop%20Music/
• There are some forbidden characters to be used in folder and files names on the server, same on the ownCloud
iOS library “”, “/”,”<”,”>”,”:”,”“”,””,”?”,”*”
• To move a folder the origin path and the destination path must end with “/”
• To move a file the origin path and the destination path must not end with “/”
• Upload and download actions may be cancelled thanks to the object “NSOperation”
• Unit tests, before launching unit tests you have to enter your account information (server url, user and password)
on OCCommunicationLibTests.m
180
Chapter 9. iOS Application Development
CHAPTER
TEN
INDICES AND TABLES
• genindex
181
ownCloud Developer Manual, Release 6.0
182
Chapter 10. Indices and tables
PHP NAMESPACE INDEX
o
OC\Files, 48
OCA\AppFramework, 96
OCA\AppFramework\Controller, 97
OCA\AppFramework\Core, 98
OCA\AppFramework\Db, 104
OCA\AppFramework\DependencyInjection,
107
OCA\AppFramework\Http, 112
OCA\AppFramework\Middleware, 116
OCA\AppFramework\Middleware\Security,
117
OCA\AppFramework\Middleware\Twig, 118
OCA\AppFramework\Utility, 122
OCP, 40
183
ownCloud Developer Manual, Release 6.0
184
PHP Namespace Index
INDEX
Symbols
method), 116
__construct()
(OCA\AppFramework\Middleware\Security\SecurityExceptio
_Model() (class), 83
method),
118
_Request() (class), 81
__construct()
(OCA\AppFramework\Middleware\Security\SecurityMiddlew
__call() (OCA\AppFramework\Db\Entity method), 106
method), 117
__construct() (OCA\AppFramework\Controller\Controller
__construct()
(OCA\AppFramework\Middleware\Twig\TwigMiddleware
method), 97
method),
118
__construct() (OCA\AppFramework\Core\API method),
__construct()
(OCA\AppFramework\Utility\FaviconFetcher
98
method), 121
__construct() (OCA\AppFramework\Db\DoesNotExistException
__construct()
(OCA\AppFramework\Utility\MethodAnnotationReader
method), 104
method),
121
__construct()
(OCA\AppFramework\Db\Mapper
__construct() (OC_OCS_Result method), 41
method), 104
__construct() (OC_Template method), 41
__construct() (OCA\AppFramework\Db\MultipleObjectsReturnedException
__construct() (OC\Files\View method), 48
method), 104
__get() (OCA\AppFramework\Http\Request method),
__construct() (OCA\AppFramework\DependencyInjection\DIContainer
114
method), 107
__construct()
(OCA\AppFramework\Http\Dispatcher __isset() (OCA\AppFramework\Http\Request method),
114
method), 107
__set() (OCA\AppFramework\Http\Request method),
__construct() (OCA\AppFramework\Http\DownloadResponse
114
method), 109
__unset() (OCA\AppFramework\Http\Request method),
__construct() (OCA\AppFramework\Http\ForbiddenResponse
114
method), 114
__construct() (OCA\AppFramework\Http\Http method),
A
108
__construct() (OCA\AppFramework\Http\JSONResponse abs_url() (built-in function), 71
activateNavigationEntry()
method), 109
(OCA\AppFramework\Core\API
method),
__construct() (OCA\AppFramework\Http\NotFoundResponse
99
method), 114
__construct() (OCA\AppFramework\Http\RedirectResponseadd() (built-in function), 83
add3rdPartyScript()
(OCA\AppFramework\Core\API
method), 110
method), 99
__construct()
(OCA\AppFramework\Http\Request
add3rdPartyStyle()
(OCA\AppFramework\Core\API
method), 113
method), 99
__construct() (OCA\AppFramework\Http\TemplateResponse
addHeader() (OC_Template method), 42
method), 111
addHeader()
(OCA\AppFramework\Http\Response
__construct() (OCA\AppFramework\Http\TextDownloadResponse
method), 108
method), 112
__construct() (OCA\AppFramework\Http\TextResponse addNavigationEntry() (OCA\AppFramework\Core\API
method), 99
method), 111
(OCA\AppFramework\Core\API
__construct() (OCA\AppFramework\Http\TwigResponse addRegularTask()
method), 103
method), 112
addScript() (OCA\AppFramework\Core\API method), 99
__construct() (OCA\AppFramework\Middleware\MiddlewareDispatcher
addStyle() (OCA\AppFramework\Core\API method), 99
185
ownCloud Developer Manual, Release 6.0
addType() (OCA\AppFramework\Db\Entity method),
method), 108
107
chroot() (OC\Files\View method), 48
afterController() (OCA\AppFramework\Middleware\Middleware
clear() (built-in function), 84
method), 115
clearHook() (OCA\AppFramework\Core\API method),
afterController() (OCA\AppFramework\Middleware\MiddlewareDispatcher
103
method), 116
columnToProperty()
(OCA\AppFramework\Db\Entity
afterController() (OCA\AppFramework\Middleware\Twig\TwigMiddleware
method), 106
method), 118
connectHook()
(OCA\AppFramework\Core\API
afterException() (OCA\AppFramework\Middleware\Middleware
method), 103
method), 115
Controller (class in OCA\AppFramework\Controller), 97
afterException() (OCA\AppFramework\Middleware\MiddlewareDispatcher
ControllerTestUtility
(class
in
method), 116
OCA\AppFramework\Utility), 119
afterException() (OCA\AppFramework\Middleware\Security\SecurityMiddleware
cookie()
(OCA\AppFramework\Controller\Controller
method), 117
method), 98
allowedKeys (OCA\AppFramework\Http\Request prop- copy() (OC\Files\View method), 51
erty), 113
count() (OCA\AppFramework\Http\Request method),
API (class in OCA\AppFramework\Core), 98
113
API (class in OCP), 40
api (OCA\AppFramework\Controller\Controller prop- D
erty), 97
data (OCA\AppFramework\Http\JSONResponse propapi (OCA\AppFramework\Http\TemplateResponse property), 109
erty), 110
delete() (built-in function), 82
api
(OCA\AppFramework\Utility\MapperTestUtility delete() (OCA\AppFramework\Db\Mapper method), 105
property), 120
deleteAll() (OC\Files\View method), 51
App (class in OCA\AppFramework), 96
detectFormfactor() (OC_Template method), 43
append() (OC_Template method), 42
DIContainer (class in OCA\AppFramework\DependencyInjection),
appName (OCA\AppFramework\Http\TemplateResponse
107
property), 110
dispatch()
(OCA\AppFramework\Http\Dispatcher
assertAnnotations() (OCA\AppFramework\Utility\ControllerTestUtility
method), 107
method), 119
Dispatcher (class in OCA\AppFramework\Http), 107
assertHeaders() (OCA\AppFramework\Utility\ControllerTestUtility
DoesNotExistException
(class
in
method), 119
OCA\AppFramework\Db), 104
assign() (OC_Template method), 42
DownloadResponse (class in OCA\AppFramework\Http),
109
B
E
beforeController() (OCA\AppFramework\Middleware\Middleware
emitHook() (OCA\AppFramework\Core\API method),
method), 115
103
beforeController() (OCA\AppFramework\Middleware\MiddlewareDispatcher
Entity (class in OCA\AppFramework\Db), 106
method), 116
env()
(OCA\AppFramework\Controller\Controller
beforeController() (OCA\AppFramework\Middleware\Security\SecurityMiddleware
method), 97
method), 117
error (OCA\AppFramework\Http\JSONResponse propbeforeEach() (OCA\AppFramework\Utility\MapperTestUtility
erty), 109
method), 120
execute() (OCA\AppFramework\Db\Mapper method),
beforeOutput() (OCA\AppFramework\Middleware\Middleware
105
method), 115
extractFromPage() (OCA\AppFramework\Utility\FaviconFetcher
beforeOutput() (OCA\AppFramework\Middleware\MiddlewareDispatcher
method), 121
method), 117
beforeOutput() (OCA\AppFramework\Middleware\Twig\TwigMiddleware
F
method), 118
buildURL() (OCA\AppFramework\Utility\FaviconFetcher FaviconFetcher (class in OCA\AppFramework\Utility),
method), 122
121
fetch()
(OCA\AppFramework\Utility\FaviconFetcher
C
method), 121
cacheFor()
(OCA\AppFramework\Http\Response fetchPage() (OC_Template method), 43
186
Index
ownCloud Developer Manual, Release 6.0
file_exists() (OC\Files\View method), 50
file_get_contents() (OC\Files\View method), 51
file_put_contents() (OC\Files\View method), 51
filemtime() (OC\Files\View method), 50
filesize() (OC\Files\View method), 50
filetype() (OC\Files\View method), 50
findOneQuery()
(OCA\AppFramework\Db\Mapper
method), 105
fopen() (OC\Files\View method), 51
ForbiddenResponse (class in OCA\AppFramework\Http),
114
free_space() (OC\Files\View method), 52
fromParams() (OCA\AppFramework\Db\Entity method),
106
fromRow() (OCA\AppFramework\Db\Entity method),
106
fromTmpFile() (OC\Files\View method), 51
getMountPoint() (OC\Files\View method), 49
getOwner() (OC\Files\View method), 53
getParams() (OCA\AppFramework\Controller\Controller
method), 97
getParams() (OCA\AppFramework\Http\JSONResponse
method), 110
getParams() (OCA\AppFramework\Http\TemplateResponse
method), 111
getPath() (OC\Files\View method), 53
getRedirectURL() (OCA\AppFramework\Http\RedirectResponse
method), 110
getRelativePath() (OC\Files\View method), 48
getRenderAs() (OCA\AppFramework\Http\TemplateResponse
method), 111
getRequest() (OCA\AppFramework\Utility\ControllerTestUtility
method), 119
getRoot() (OC\Files\View method), 48
getStatus()
(OCA\AppFramework\Http\Response
G
method), 109
getStatusHeader()
(OCA\AppFramework\Http\Http
get() (built-in function), 82, 84
method), 108
getAbsolutePath() (OC\Files\View method), 48
(OCA\AppFramework\Core\API
getAbsoluteURL()
(OCA\AppFramework\Core\API getSystemValue()
method), 99
method), 101
getTableName()
(OCA\AppFramework\Db\Mapper
getAll() (built-in function), 84
method), 104
getAPIMock() (OCA\AppFramework\Utility\TestUtility
getTemplate() (OCA\AppFramework\Core\API method),
method), 120
102
getAppName()
(OCA\AppFramework\Core\API
getTemplateName() (OCA\AppFramework\Http\TemplateResponse
method), 99
method), 111
getAppValue() (OCA\AppFramework\Core\API method),
getTime()
(OCA\AppFramework\Utility\TimeFactory
100
method), 122
getById() (built-in function), 83
getTrans() (OCA\AppFramework\Core\API method), 100
getCore() (OCA\AppFramework\Utility\SimplePieAPIFactory
getUpdatedFields()
(OCA\AppFramework\Db\Entity
method), 122
method), 106
getDirectoryContent() (OC\Files\View method), 52
getUploadedFile() (OCA\AppFramework\Controller\Controller
getETag() (OC\Files\View method), 53
method), 97
getETag()
(OCA\AppFramework\Http\Response
getUrlContent()
(OCA\AppFramework\Core\API
method), 109
method), 103
getFile() (OCA\AppFramework\Utility\SimplePieAPIFactory
getUserId() (OCA\AppFramework\Core\API method), 99
method), 122
getUserValue()
(OCA\AppFramework\Core\API
getFileInfo() (OC\Files\View method), 52
method), 100
getFormFactorExtension() (OC_Template method), 43
getHeaders()
(OCA\AppFramework\Http\Response
H
method), 108
getInsertId() (OCA\AppFramework\Core\API method), hasAnnotation() (OCA\AppFramework\Utility\MethodAnnotationReader
method), 121
101
getLastModified() (OCA\AppFramework\Http\Response hash() (OC\Files\View method), 52
hasUpdated() (OC\Files\View method), 52
method), 109
headers (OCA\AppFramework\Http\Http property), 108
getLocalFile() (OC\Files\View method), 49
getLocalFilePath()
(OCA\AppFramework\Core\API html_select_options() (global function), 45
Http (class in OCA\AppFramework\Http), 108
method), 102
human_file_size() (global function), 45
getLocalFolder() (OC\Files\View method), 49
getMiddlewares() (OCA\AppFramework\Middleware\MiddlewareDispatcher
I
method), 116
getMimeType() (OC\Files\View method), 52
id (OCA\AppFramework\Db\Entity property), 106
Index
187
ownCloud Developer Manual, Release 6.0
image_path() (built-in function), 72
MiddlewareDispatcher
(class
in
image_path() (global function), 45
OCA\AppFramework\Middleware), 116
imagePath() (OCA\AppFramework\Core\API method), mimetype_icon() (global function), 46
101
mkdir() (OC\Files\View method), 49
inc() (OC_Template method), 43
MultipleObjectsReturnedException
(class
in
insert() (OCA\AppFramework\Db\Mapper method), 105
OCA\AppFramework\Db), 104
is_dir() (OC\Files\View method), 49
N
is_file() (OC\Files\View method), 50
isAdminUser()
(OCA\AppFramework\Core\API NotFoundResponse (class in OCA\AppFramework\Http),
method), 102
114
isAjax() (OCA\AppFramework\Middleware\Security\SecurityException
O
method), 118
isAppEnabled()
(OCA\AppFramework\Core\API OC_OCS_Result (class), 41
method), 102
OC_Template (class), 41
isCreatable() (OC\Files\View method), 50
OC\Files (namespace), 48
isDeletable() (OC\Files\View method), 50
OCA\AppFramework (namespace), 96
isImage() (OCA\AppFramework\Utility\FaviconFetcher OCA\AppFramework\Controller (namespace), 97
method), 121
OCA\AppFramework\Core (namespace), 98
isLoggedIn() (OCA\AppFramework\Core\API method), OCA\AppFramework\Db (namespace), 104, 106
102
OCA\AppFramework\DependencyInjection
(namesisReadable() (OC\Files\View method), 50
pace), 107
isSharable() (OC\Files\View method), 50
OCA\AppFramework\Http (namespace), 107–114
isSubAdminUser()
(OCA\AppFramework\Core\API OCA\AppFramework\Middleware (namespace), 115, 116
method), 102
OCA\AppFramework\Middleware\Security (namespace),
isUpdatable() (OC\Files\View method), 50
117, 118
items (OCA\AppFramework\Http\Request property), 113 OCA\AppFramework\Middleware\Twig
(namespace),
118
J
OCA\AppFramework\Utility (namespace), 119–122
JSONResponse (class in OCA\AppFramework\Http), 109 OCP (namespace), 40
offsetExists()
(OCA\AppFramework\Http\Request
L
method), 113
offsetGet() (OCA\AppFramework\Http\Request method),
link_to() (built-in function), 73
113
link_to() (global function), 46
offsetSet()
(OCA\AppFramework\Http\Request method),
linkTo() (OCA\AppFramework\Core\API method), 101
113
linkToAbsolute()
(OCA\AppFramework\Core\API
offsetUnset()
(OCA\AppFramework\Http\Request
method), 101
method),
113
linkToRoute() (OCA\AppFramework\Core\API method),
opendir() (OC\Files\View method), 49
101
openEventSource()
(OCA\AppFramework\Core\API
log() (OCA\AppFramework\Core\API method), 102
method), 103
M
main() (OCA\AppFramework\App method), 96
Mapper (class in OCA\AppFramework\Db), 104
MapperTestUtility
(class
in
OCA\AppFramework\Utility), 120
markFieldUpdated()
(OCA\AppFramework\Db\Entity
method), 106
method()
(OCA\AppFramework\Controller\Controller
method), 97
MethodAnnotationReader
(class
in
OCA\AppFramework\Utility), 121
Middleware (class in OCA\AppFramework\Middleware),
115
188
P
p() (global function), 47
params (OCA\AppFramework\Http\TemplateResponse
property), 110
params()
(OCA\AppFramework\Controller\Controller
method), 97
passesCSRFCheck()
(OCA\AppFramework\Core\API
method), 102
post() (built-in function), 82
prepareQuery()
(OCA\AppFramework\Core\API
method), 100
print_unescaped() (global function), 47
printAdminPage() (OC_Template method), 44
Index
ownCloud Developer Manual, Release 6.0
printGuestPage() (OC_Template method), 44
SecurityException
(class
in
printPage() (OC_Template method), 43
OCA\AppFramework\Middleware\Security),
printUserPage() (OC_Template method), 44
118
propertyToColumn() (OCA\AppFramework\Db\Entity SecurityMiddleware
(class
in
method), 106
OCA\AppFramework\Middleware\Security),
put() (built-in function), 82
117
putFileInfo() (OC\Files\View method), 52
session()
(OCA\AppFramework\Controller\Controller
method), 98
R
setAppValue() (OCA\AppFramework\Core\API method),
100
readdir() (OC\Files\View method), 49
setErrorMessage() (OCA\AppFramework\Http\JSONResponse
readfile() (OC\Files\View method), 50
method), 110
RedirectResponse (class in OCA\AppFramework\Http),
setETag()
(OCA\AppFramework\Http\Response
110
method), 109
register() (OCP\API method), 40
registerAdmin()
(OCA\AppFramework\Core\API setItemsPerPage() (OC_OCS_Result method), 41
setLastModified() (OCA\AppFramework\Http\Response
method), 104
method), 109
registerMiddleware() (OCA\AppFramework\Middleware\MiddlewareDispatcher
setMapperResult() (OCA\AppFramework\Utility\MapperTestUtility
method), 116
method), 120
relative_modified_date() (global function), 47
setParams() (OCA\AppFramework\Http\JSONResponse
rename() (OC\Files\View method), 51
method), 109
render()
(OCA\AppFramework\Controller\Controller
setParams() (OCA\AppFramework\Http\TemplateResponse
method), 98
method), 111
render()
(OCA\AppFramework\Http\JSONResponse
setStatus()
(OCA\AppFramework\Http\Response
method), 110
method), 108
render() (OCA\AppFramework\Http\Response method),
setSystemValue()
(OCA\AppFramework\Core\API
108
method), 100
render() (OCA\AppFramework\Http\TemplateResponse
setTotalItems() (OC_OCS_Result method), 41
method), 111
(OCA\AppFramework\Core\API
render() (OCA\AppFramework\Http\TextDownloadResponsesetUserValue()
method), 100
method), 112
render()
(OCA\AppFramework\Http\TextResponse simple_file_size() (global function), 48
SimplePieAPIFactory
(class
in
method), 112
OCA\AppFramework\Utility), 122
render()
(OCA\AppFramework\Http\TwigResponse
size() (built-in function), 84
method), 112
renderAs (OCA\AppFramework\Http\TemplateResponse stat() (OC\Files\View method), 50
style() (built-in function), 72
property), 110
renderAs() (OCA\AppFramework\Http\TemplateResponse
T
method), 111
renderJSON() (OCA\AppFramework\Controller\Controller templateName (OCA\AppFramework\Http\TemplateResponse
method), 98
property), 110
Request (class in OCA\AppFramework\Http), 113
TemplateResponse (class in OCA\AppFramework\Http),
request
(OCA\AppFramework\Controller\Controller
110
property), 97
TestUtility (class in OCA\AppFramework\Utility), 120
request() (built-in function), 81
TextDownloadResponse
(class
in
resetUpdatedFields() (OCA\AppFramework\Db\Entity
OCA\AppFramework\Http), 112
method), 106
TextResponse (class in OCA\AppFramework\Http), 111
resolvePath() (OC\Files\View method), 49
TimeFactory (class in OCA\AppFramework\Utility), 122
Response (class in OCA\AppFramework\Http), 108
toTmpFile() (OC\Files\View method), 51
rmdir() (OC\Files\View method), 49
touch() (OC\Files\View method), 51
trans() (built-in function), 72
S
TwigMiddleware
(class
in
OCA\AppFramework\Middleware\Twig),
script() (built-in function), 72
118
search() (OC\Files\View method), 53
TwigResponse
(class in OCA\AppFramework\Http), 112
searchByMime() (OC\Files\View method), 53
Index
189
ownCloud Developer Manual, Release 6.0
U
unlink() (OC\Files\View method), 51
update() (built-in function), 83
update() (OCA\AppFramework\Db\Mapper method), 105
url() (built-in function), 71
V
View (class in OC\Files), 48
190
Index