mirror of
https://github.com/nextcloud/documentation.git
synced 2026-01-04 02:36:49 +07:00
Add docs for projects
This commit is contained in:
216
developer_manual/digging_deeper/projects.rst
Normal file
216
developer_manual/digging_deeper/projects.rst
Normal file
@@ -0,0 +1,216 @@
|
||||
========
|
||||
Projects
|
||||
========
|
||||
|
||||
Projects are a way to link items from different apps via a common interface.
|
||||
|
||||
App developers can integrate into projects and provide their own entity types to link with.
|
||||
|
||||
|
||||
Register a resource provider
|
||||
---------------------------
|
||||
|
||||
Things like files, deck cards and talk rooms are called Resources in projects.
|
||||
In order to add your own resource type, we need to create a class implementing the
|
||||
`OCP\Collaboration\Resources\IProvider` interface.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace OCA\MyApp\Collaboration\Resources;
|
||||
|
||||
|
||||
use OCP\Collaboration\Resources\IProvider;
|
||||
use OCP\Collaboration\Resources\IResource;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
|
||||
class MyResourceProvider implements IProvider {
|
||||
public const RESOURCE_TYPE = 'my-app-item';
|
||||
|
||||
/**
|
||||
* @var IURLGenerator
|
||||
*/
|
||||
private $url;
|
||||
|
||||
public function __construct(IURLGenerator $url) {
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getType(): string {
|
||||
return self::RESOURCE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getResourceRichObject(IResource $resource): array {
|
||||
$item = $this->getItem($resource);
|
||||
$icon = $this->url->linkToRouteAbsolute('myapp.images.get_icon', ['id' => $item->getId()]);
|
||||
$resourceUrl = $this->url->linkToRouteAbsolute('bookmarks.page.index', ['item' => $bookmark->getId()]);
|
||||
|
||||
return [
|
||||
'type' => self::RESOURCE_TYPE,
|
||||
'id' => $resource->getId(),
|
||||
'name' => $item->getTitle(),
|
||||
'link' => $resourceUrl,
|
||||
'iconUrl' => $favicon,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function canAccessResource(IResource $resource, ?IUser $user): bool {
|
||||
if ($resource->getType() !== self::RESOURCE_TYPE || !($user instanceof IUser)) {
|
||||
return false;
|
||||
}
|
||||
$bookmark = $this->getItem($resource);
|
||||
if ($bookmark === null) {
|
||||
return false;
|
||||
}
|
||||
return $bookmark->getUserId() === $user->getUID()
|
||||
}
|
||||
|
||||
private function getItem(IResource $resource) : ?Item {
|
||||
// implement me
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
The `MyResourceProvider` class needs to be registered during the :ref:`app bootstrap<Bootstrapping>`.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\MyApp\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
use OCA\MyApp\Dashboard\MyAppWidget;
|
||||
|
||||
class Application extends App implements IBootstrap {
|
||||
|
||||
public const APP_ID = 'myapp';
|
||||
|
||||
public function __construct(array $urlParams = []) {
|
||||
parent::__construct(self::APP_ID, $urlParams);
|
||||
}
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
$context->injectFn(Closure::fromCallable([$this, 'registerCollaborationResources']));
|
||||
}
|
||||
|
||||
protected function registerCollaborationResources(IProviderManager $resourceManager, SymfonyAdapter $symfonyAdapter): void {
|
||||
$resourceManager->registerResourceProvider(ResourceProvider::class);
|
||||
|
||||
$symfonyAdapter->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', static function () {
|
||||
Util::addScript(self::APP_ID, 'collections');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
As you can see we also already register a front-end script, which we are going to create next.
|
||||
|
||||
|
||||
Provide a user interface
|
||||
------------------------
|
||||
|
||||
The user interface can be registered through the public `OCP.Collaboration.registerType`
|
||||
JavaScript method. The first parameter represents the resource type that has already
|
||||
been specified in the `IResourceProvider` implementation. The second parameter is an object with
|
||||
three properties:
|
||||
|
||||
* `typeString` A localized string that will be displayed in the dropdown when choosing which resource type to link to
|
||||
* `typeIconClass` A CSS class of the icon that should be used for this entry
|
||||
* `action` An async function that will produce a resource picker UI and resolves with the resource id
|
||||
|
||||
The following example shows how a Vue.js component could be used to render the
|
||||
widget user interface, however this approach works for any other framework as well
|
||||
as plain JavaScript:
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
import Vue from 'vue'
|
||||
import ItemPickerDialog from './components/ItemPickerDialog'
|
||||
|
||||
OCP.Collaboration.registerType('myapp', {
|
||||
action: () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const container = document.createElement('div')
|
||||
container.id = 'myapp-item-select'
|
||||
const body = document.getElementById('body-user')
|
||||
body.appendChild(container)
|
||||
const ComponentVM = new Vue({
|
||||
render: h => h(ItemPickerDialog),
|
||||
})
|
||||
ComponentVM.$mount(container)
|
||||
ComponentVM.$root.$on('close', () => {
|
||||
ComponentVM.$el.remove()
|
||||
ComponentVM.$destroy()
|
||||
reject(new Error('User cancelled resource selection'))
|
||||
})
|
||||
ComponentVM.$root.$on('select', (id) => {
|
||||
resolve(id)
|
||||
ComponentVM.$el.remove()
|
||||
ComponentVM.$destroy()
|
||||
})
|
||||
})
|
||||
},
|
||||
typeString: t('myapp', 'Link to an item'),
|
||||
typeIconClass: 'icon-file',
|
||||
})
|
||||
|
||||
This will allow other apps to link to your items. We also want to link to other apps' items.
|
||||
Since all apps with projects support are listening on the above loadAdditionalScripts event,
|
||||
we can simply dispatch it when we render our main page template.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
\OC::$server->getEventDispatcher()->dispatch('\OCP\Collaboration\Resources::loadAdditionalScripts');
|
||||
|
||||
In our Vue app, we can then render the pre-built projects picker available in the npm package `nextcloud-vue-collections`.
|
||||
|
||||
.. code-block:: vue
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<CollectionList v-if="itemId"
|
||||
:id="itemId"
|
||||
:name="itemTitle"
|
||||
type="myapp" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { CollectionList } from 'nextcloud-vue-collections'
|
||||
export default {
|
||||
name: 'CollaborationView',
|
||||
components: {
|
||||
CollectionList,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user