ownCloud Developer Manual

yrekazzyzxamuckInternet και Εφαρμογές Web

4 Δεκ 2013 (πριν από 3 χρόνια και 8 μήνες)

648 εμφανίσεις

ownCloud Developer Manual
Release 5.0
The ownCloud developers
July 15,2013
CONTENTS
1 Developer Documentation 1
1.1 Index and Tables.............................................1
2 App Developement 3
2.1 General..................................................3
2.2 App Developement Intro.........................................21
2.3 App Developement (ownCloud App API)................................24
2.4 App Developement (App Framework)..................................52
2.5 Intro...................................................124
2.6 App Development using ownCloud App API..............................124
2.7 App Development using the App Framework App...........................125
2.8 Additional APIs.............................................127
3 Core Developement 129
3.1 App config................................................129
3.2 Translation................................................131
3.3 Theming.................................................133
3.4 Unit-Testing...............................................135
4 ownCloud Test Pilots 137
4.1 Why do you want to join.........................................137
4.2 Who can join...............................................137
4.3 How do you join.............................................137
4.4 What do you do.............................................138
5 Bugtracker 139
5.1 Code Reviews on GitHub........................................139
5.2 Kanban Board..............................................140
6 Help and Communication 145
6.1 Mailing lists...............................................145
6.2 IRC channels...............................................145
6.3 Maintainers................................................145
7 Indices and tables 147
PHP Namespace Index 149
Index 151
i
ii
CHAPTER
ONE
DEVELOPER DOCUMENTATION
If you want to contribute please read the Contributor agreement
App Developement Develop apps
for ownCloud and publish on
the ownCloud appstore
Core Developement Develop on the
ownCloud internals
Documentation Create and enhance
documentation
ownCloud Test Pilots Help us to test
ownCloud by joining the test-
ing 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
1.1 Index and Tables
• genindex
• modindex
1
ownCloud Developer Manual,Release 5.0
2 Chapter 1.Developer Documentation
CHAPTER
TWO
APP DEVELOPEMENT
2.1 General
2.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:Programdefensively: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
3
ownCloud Developer Manual,Release 5.0
inject HTML/JavaScript into the page to steal the users session,log keyboard entries,even performDDOS 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 DOMmanipula-
tion.
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.
To prevent XSS in your app,never use echo,print() or <%= - use p() instead which will sanitize the input.
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
messageTd.text(username);
It may also be wise to choose a proper JavaScript framework like AngularJS which automatically handles the
JavaScript escaping for you.
4 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
Clickjacking
Clickjacking tricks the user to click into an invisible iframe to performan 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 fromthe 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
<?php
$username = OC_User::getUser();
$file = str_replace(array(’/’,’\\’),’’,$_GET[’file’]);
fopen("/data/".$username."/".$file.".txt");
2.1.General 5
ownCloud Developer Manual,Release 5.0
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 performnot 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
Using the App Framework,these checks are already automatically performed for each request and have to be ex-
plicitely 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).
6 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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 formis 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 IRCchannel#owncloud-dev
on Freenode.
2.1.2 Coding Style & General Guidelines
General
• Maximumline-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
2.1.General 7
ownCloud Developer Manual,Release 5.0
• 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 --rebaseto avoid generating extra commits like:merged master
into master
• We need a signed contributor agreement fromyou 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 fromMozilla.
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.
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
8 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
*
@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,
dont use spaces
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
?>
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
2.1.General 9
ownCloud Developer Manual,Release 5.0
<?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 programis 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
• 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 || {};
10 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
(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’);
};
var child = new ChildObject(’toni’,23);
//prints:
//toni
//childobject
child.sayHello();
2.1.General 11
ownCloud Developer Manual,Release 5.0
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’//false
0 == ’’//true
0 == ’0’//true
false == ’false’//false
false == ’0’//true
false == undefined//false
false == null//false
null == undefined//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:
//single line if
if (myVar === ’hi’) {
myVar = ’ho’;
} else {
myVar = ’bye’;
}
12 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
//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;
}
#content.myHeader ul li.list_item {
color:red;
display:inline-block;
}
TBD
2.1.General 13
ownCloud Developer Manual,Release 5.0
2.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,
frombeing 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.
2.1.4 AngularJS
AngularJS is an MV* JavaScript framework by Google.
Documentation is available at these sources:
• Official tutorial
14 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
• Developer guide
• API reference
• Screencasts on Youtube
• More screencasts
• Collection of resources
What problemdoes 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 problemis caused by jQueries habit to operate directly on domelements.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 problemis the dynamic generation of DOMelements.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 fromthe 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
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 DOMby 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.
2.1.General 15
ownCloud Developer Manual,Release 5.0
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 Xelements 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.
2.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.Agood overviewover howit works and what the benefits are can be read on Google’s
Clean Code Talks
16 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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.
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.
2.1.General 17
ownCloud Developer Manual,Release 5.0
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:
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 {
18 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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 = ’’;
while($row = $result->fetchRow()){
$output.= ’<p>’.htmlentities($row[’prename’]).’</p>’;
}
return $output;
}
}
2.1.General 19
ownCloud Developer Manual,Release 5.0
//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 systemhandle the rest.
Pimple is a simple container implementation.The documentation on howto 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.Fromthe Pimple Tutorial:
By default,each time you get an object,Pimple returns a new instance of it.If you want the same instance to be returned for all calls,wrap your anonymous function with the share() method
Do not inject the container
Injecting the container as a dependency is known as the Service Locator Pattern which is widely regarded as an anti-
pattern.
It makes your code dependant on the container and hides the class’ real dependencies.This makes testing and main-
taining harder.
20 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
2.2 App Developement Intro
2.2.1 Getting Started
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 mailing list so other contributors might join in.
This tutorial uses the App Framework app,a small framework that makes developing apps easier.To use it,it has to
be enabled on the apps settings page.
Get the sources
There are two ways to obtain ownCloud:
• Using the stable version
• Using the developement version fromGitHub
Using stable
Install the current version of ownCloud.
Using development version (recommended)
First set up your webserver and database (Section:Manual Installation - Prerequisites).
ownCloud’s source is hosted on GitHub.To be able to participate or check out the code You will have to sign up on
GitHub and install Git.For help on git,contact the GitHub Help Page.
To get started the basic git repositories need to cloned into the webserver’s directory.Depending on the distro this will
either be
•/var/www
•/var/www/html
•/srv/http
and the apache user and group for the chown command will either be
• http
• www-data
• apache
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 apps
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
2.2.App Developement Intro 21
ownCloud Developer Manual,Release 5.0
Now restart the apache server and get ready to set up ownCloud at http://localhost/owncloud.
Enable debugging 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 fromcore.Always check owncloud/config/config.php afterwards.
2.2.2 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/
Using the scaffolding tool
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
Manual file creation
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:
22 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
whoami
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.
2.2.App Developement Intro 23
ownCloud Developer Manual,Release 5.0
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
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
ownCloud app API
App Framework
Difficulty
easy
medium
Architecture
routes and templates
routes and MVC
Testability
hard
easy:built-in Dependency Injection and TDD tools
Maintainability
hard
easy
Templates
OC_Template
OC_Template and Twig
Security
manual checks
escapse XSS (Twig only),does CSRF and Authentication checks by default
2.3 App Developement (ownCloud App API)
2.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’),
24 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
//the title of your application.This will be used in the
//navigation or on the settings page of your app
’name’ => ’My App’
));
Create the main route
Routes map the URL to functions and allowto extract values.To showthe 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.
2.3.2 App Metadata
The appinfo/info.xml contains metadata about the app:
2.3.App Developement (ownCloud App API) 25
ownCloud Developer Manual,Release 5.0
<?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>
<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
26 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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 filesystemfunctionality (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.The id is appended to the URL/own-
cloud/index.php/public.Example with id set to ‘calendar’:
/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.
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 themafter the installation.
shipped
Core apps only:Used to tell ownCloud that the app is in the standard release
2.3.App Developement (ownCloud App API) 27
ownCloud Developer Manual,Release 5.0
2.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 fromthe 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.
2.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 fromthe URLs.
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’;
}
);
28 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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 fromthe 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)
2.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.xmlin MDB2’s XMLscheme 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>
2.3.App Developement (ownCloud App API) 29
ownCloud Developer Manual,Release 5.0
<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.
2.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:
<?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:
30 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
<?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();
2.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 vari-
ables 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
2.3.App Developement (ownCloud App API) 31
ownCloud Developer Manual,Release 5.0
2.3.8 Static content
Static content consists of:
• img/:all images
• js/:all JavaScript files
• css/:all CSS files
CSS and JavaScript are compressed by ownCloud so if the CSS or JavaScript do not seemto get updated,check if the
debug mode is enabled.To enable it see Getting Started
2.3.9 CSS
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
Formfactors
ownCloud automatically detects what kind of formfactor 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:
32 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
script.tablet.css
2.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.
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:password@yourowncloud.com/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 fromthe application is wrapped inside a data element:
XML:
2.3.App Developement (ownCloud App API) 33
ownCloud Developer Manual,Release 5.0
<?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
2.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 filesystemfunctions directly will be sufficient,such as unlink() and mkdir().Most
of the time however it is necessary to use one of ownCloud’s filesystemclasses.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
34 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
2.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
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
2.3.App Developement (ownCloud App API) 35
ownCloud Developer Manual,Release 5.0
• 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
2.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
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 fromthe 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:
36 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
<?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 *PRE-
FIX*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
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.
2.3.App Developement (ownCloud App API) 37
ownCloud Developer Manual,Release 5.0
Import
Import is a little more tricky as we have to take into account data fromdifferent 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;
}
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
newid of the bookmark is saved in an array,with the key being the old id of the bookmark.This means when inserting
38 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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
2.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
• $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
2.3.App Developement (ownCloud App API) 39
ownCloud Developer Manual,Release 5.0
__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 re-
sult)
setTotalItems($items)
Sets the <totalitems> value in the response.Use this to informthe 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:
<?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
40 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
Add a customelement 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’);
$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:
2.3.App Developement (ownCloud App API) 41
ownCloud Developer Manual,Release 5.0
<?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
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 ])
42 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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:
Todo
provide example
Template functions
These functions are automatically available in all templates.
2.3.App Developement (ownCloud App API) 43
ownCloud Developer Manual,Release 5.0
html_select_options
html_select_options($options,$selected[,$params ])
Parameters
• $options (array) – an array of the formvalue => 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
•/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
44 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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 fromyour 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:
<img src="<?php print_unescaped(
mimetype_icon(’application/xml’);
);?>"/>
p
p($data)
Parameters
• $data – the variable/array/object that should be printed
2.3.App Developement (ownCloud App API) 45
ownCloud Developer Manual,Release 5.0
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 fromwhomwe 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>
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
Amore 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.
46 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
<?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
get path relative to the root of the view
getMountPoint($path)
Parameters
• $path (string) –
Returns string
2.3.App Developement (ownCloud App API) 47
ownCloud Developer Manual,Release 5.0
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) –
is_dir($path)
Parameters
• $path (mixed) –
is_file($path)
Parameters
48 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
• $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) –
filemtime($path)
Parameters
• $path (mixed) –
touch($path,$mtime=null)
Parameters
2.3.App Developement (ownCloud App API) 49
ownCloud Developer Manual,Release 5.0
• $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) –
fromTmpFile($tmpFile,$path)
Parameters
• $tmpFile (mixed) –
• $path (mixed) –
getMimeType($path)
50 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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- mimetype-
encrypted- versioned
get the filesysteminfo
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) –
• $data (array) –
Returns int returns the fileid of the updated file
change file metadata
search($query)
Parameters
2.3.App Developement (ownCloud App API) 51
ownCloud Developer Manual,Release 5.0
• $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
2.4 App Developement (App Framework)
2.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:
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!
52 Chapter 2.App Developement
ownCloud Developer Manual,Release 5.0
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 Notes app because the App Framework App is disabled’;
\OCP\Util::writeLog(’myapp’,$msg,\OCP\Util::ERROR);