serlo-org / athene2-versioning
Zend Framework 2 Module that provides versioning components for Athene2
Requires
- php: >=5.4
- doctrine/common: ~2.4
- doctrine/doctrine-module: ~0.8
- doctrine/doctrine-orm-module: ~0.8
- serlo-org/athene2-common: dev-master
- zendframework/zend-authentication: ~2.3
- zendframework/zend-eventmanager: ~2.3
- zendframework/zend-filter: ~2.3
- zendframework/zend-form: ~2.3
- zendframework/zend-mvc: ~2.3
- zendframework/zend-paginator: ~2.3
- zendframework/zend-servicemanager: ~2.3
- zendframework/zend-stdlib: ~2.3
- zendframework/zend-validator: ~2.3
- zf-commons/zfc-rbac: 2.3
Requires (Dev)
- phpunit/phpunit: ~3.7
- satooshi/php-coveralls: ~0.6
- squizlabs/php_codesniffer: 1.4.*
- zendframework/zendframework: ~2.2
This package is not auto-updated.
Last update: 2020-03-02 04:28:28 UTC
README
Installation
athene2-versioning only officially supports installation through Composer. For Composer documentation, please refer to getcomposer.org.
Install the module:
$ php composer.phar require serlo-org/athene2-versioning:~2.0
Using the versioning module
The versioning module enables you to manage repositories which contain revisions. Each repository has n revisions and one or zero HEAD revision. The HEAD revision is the current revision of that repository. The default implementation of the VersioningManager is Doctrine friendly!
Features
- Doctrine implementation (using the ObjectManager)
- Bundled with zfc-rbac for authorization
- Events
Understanding how it works
The versioning module consists of one Versioning\Manager\VersioningManager
, who implements the Versioning\Manager\VersioningManagerInterface
. He manages models or entities which implement the Versioning\Entity\RepositoryInterface
and the Versioning\Entity\RevisionInterface
.
Let's implement those entity interfaces!
You can find example implementations here!
RevisionInterface
<?php use Athene2\Versioning\Entity\RepositoryInterface; use Athene2\Versioning\Entity\RevisionInterface; use ZfcRbac\Identity\IdentityInterface; /** * Class Revision * * @author Aeneas Rekkas */ class Revision implements RevisionInterface { /** * @var mixed */ protected $id; /** * @var RepositoryInterface */ protected $repository; /** * @var */ protected $author; /** * @var bool */ protected $trashed = false; /** * @var array */ protected $data = []; /** * @param mixed $id * @return void */ public function setId($id) { $this->id = $id; } /** * {@inheritDoc} */ public function getId() { return $this->id; } /** * {@inheritDoc} */ public function getRepository() { return $this->repository; } /** * {@inheritDoc} */ public function setRepository(RepositoryInterface $repository) { $this->repository = $repository; } /** * {@inheritDoc} */ public function setAuthor(IdentityInterface $author) { $this->author = $author; } /** * {@inheritDoc} */ public function getAuthor() { return $this->author; } /** * {@inheritDoc} */ public function setTrashed($trash) { $this->trashed = (bool)$trash; } /** * {@inheritDoc} */ public function isTrashed() { return $this->trashed; } /** * {@inheritDoc} */ public function set($key, $value) { $this->data[$key] = $value; } /** * {@inheritDoc} */ public function get($key) { return isset($this->data[$key]) ? $this->data[$key] : null; } }
RepositoryInterface
<?php use Athene2\Versioning\Entity\RepositoryInterface; use Athene2\Versioning\Entity\RevisionInterface; class Repository implements RepositoryInterface { /** * @var array|RevisionInterface[] */ protected $revisions = []; /** * @var null|RevisionInterface */ protected $head = null; /** * @var mixed */ protected $id; /** * {@inheritDoc} */ public function addRevision(RevisionInterface $revision) { $this->revisions[$revision->getId()] = $revision; } /** * {@inheritDoc} */ public function createRevision() { return new Revision(); } /** * {@inheritDoc} */ public function getCurrentRevision() { return $this->head; } /** * {@inheritDoc} */ public function getId() { return $this->id; } /** * {@inheritDoc} */ public function getRevisions() { return $this->revisions; } /** * {@inheritDoc} */ public function hasCurrentRevision() { return null !== $this->head; } /** * {@inheritDoc} */ public function removeRevision(RevisionInterface $revision) { unset($this->revisions[$revision->getId()]); } /** * {@inheritDoc} */ public function setCurrentRevision(RevisionInterface $revision) { $this->head = $revision; } }
Well, that wasn't so hard, was it?
Using the RepositoryManager
The default RepositoryManager
implementation is bundled with Doctrine, ZF2 EventManager and zfc-rbac.
Setting up permissions
Not everyone should be allowed to commit, reject and accept revisions, right? Therefore, the RepositoryManager
is able to handle permissions via zfc-rbac!
To set up permissions, you will need to add some config data to your module.config.php:
return [ // ... 'versioning' => [ 'permissions' => [ // Use the classname of the revision class // In the example above the namespace is missing, therefore the classname is only "Revision". // This could be also "MyModule\Entity\Revision" 'Revision' => [ // There are three actions which need authentication: // 'commit' gets checked when you call "commitRevision" ModuleOptions::KEY_PERMISSION_COMMIT => 'revision.create', // 'checkout' gets checked when you call "checkoutRevision" ModuleOptions::KEY_PERMISSION_CHECKOUT => 'revision.checkout', // 'reject' gets checked when you call "rejectRevision" ModuleOptions::KEY_PERMISSION_REJECT => 'revision.trash' // Name the permissions whatever you like. Just be aware that they are registered in zfc-rbac! // ModuleOptions::KEY_PERMISSION_COMMIT => 'mymodule.entity.revision.commit', ] ] ] // ... ];
Important: The revision is always passed to zfc-rbac as a context object for usage with e.g. Assertions!
Create a new Revision and fill it with data!
// Let's create a repository first $repository = new Repository(); // Now we need the RepositoryManager $repositoryManager = $serviceManager->get('Athene2\Versioning\Manager\VersioningManager'); // Let's create our first revision! $revision = $repositoryManager->commitRevision($repository, ['foo' => 'bar'], 'I added some stuff'); // And check it out (set as HEAD / current revision) // We can also add a short message, why we checked out this revision! $repositoryManager->checkoutRevision($repository, $revision, 'That\'s a nice reason, isn\'t it?'); // Now, let's make those changes persistent! $repositoryManager->flush();
Trash a revision
Someone made a mistake? Just reject the revision!
// Now we need the RepositoryManager $repositoryManager = $serviceManager->get('Athene2\Versioning\Manager\VersioningManager'); $revision = $repositoryManager->rejectRevision($repository, 5, 'Sorry but there are too many mistakes!'); // Now, let's make those changes persistent! $repositoryManager->flush();
Check out a revision
Do you approve of a certain revision? Go ahead and check it out!
// Now we need the RepositoryManager $repositoryManager = $serviceManager->get('Athene2\Versioning\Manager\VersioningManager'); $revision = $repositoryManager->checkoutRevision($repository, 5, 'Fine job!'); // Now, let's make those changes persistent! $repositoryManager->flush();
Hooking into Events
The VersioningManager fires events for both failure and success:
$eventManager = $repositoryManager->getEventManager(); $eventManager->attach(VersioningEvent::COMMIT, function(VersioningEvent $event) { echo "I just committed a new revision with a cool message: " . $event->getMessage(); }); $eventManager->attach(VersioningEvent::COMMIT_UNAUTHORIZED, function(VersioningEvent $event) { echo "I just committed a new revision but didn't have the rights to do so!"; }); $repositoryManager->commitRevision($repository, $data, $message);
There are also other events available, like:
VersioningEvent::COMMIT
andVersioningEvent::COMMIT_UNAUTHORIZED
VersioningEvent::REJECT
andVersioningEvent::REJECT_UNAUTHORIZED
VersioningEvent::CHECKOUT
andVersioningEvent::CHECKOUT_UNAUTHORIZED
To be done
There are a more things to come:
- A beautiful UI to manage your repositories
- A RESTful API
- Better docs