diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index b9f5106a..32aaddc2 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -646,4 +646,54 @@ public function iCloneNodeFromTo($arg1, $arg2, $arg3) $workspace = $session->getWorkspace(); $workspace->cloneFrom($arg2, $arg1, $arg3, false); } + + /** + * @Given /^the node "([^"]*)" is locked$/ + */ + public function theNodeIsLocked($arg1) + { + $session = $this->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + $lockManager->lock($arg1, true, true); + } + + /** + * @Given /^the node "([^"]*)" is not locked$/ + */ + public function theNodeIsNotLocked($arg1) + { + $session = $this->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + if ($lockManager->isLocked($arg1)) { + $lockManager->unlock($arg1); + } + } + + /** + * @Given /^the node "([^"]*)" should be locked$/ + */ + public function theNodeShouldBeLocked($arg1) + { + $session = $this->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + $isLocked = $lockManager->isLocked($arg1); + + PHPUnit_Framework_Assert::assertTrue($isLocked); + } + + /** + * @Given /^the node "([^"]*)" should not be locked$/ + */ + public function theNodeShouldNotBeLocked($arg1) + { + $session = $this->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + $isLocked = $lockManager->isLocked($arg1); + + PHPUnit_Framework_Assert::assertFalse($isLocked); + } } diff --git a/features/lock_info.feature b/features/lock_info.feature new file mode 100644 index 00000000..f80b632c --- /dev/null +++ b/features/lock_info.feature @@ -0,0 +1,29 @@ +Feature: Show the details of a lock + In order to show the details of a lock + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + And the "session_data.xml" fixtures are loaded + And the node at "/tests_general_base" has the mixin "mix:lockable" + And the node "/tests_general_base" is locked + + Scenario: Create a new node + Given I execute the "lock:info /tests_general_base" command + Then the command should fail + And I should see the following: + """ + Not implemented + """ + # And I should see the following: + # """ + # Lock Owner: admin + # Lock Token: foobar + # Seconds Remaining: 123 + # Deep?: yes + # Live?: yes + # Owned by current session?: no + # Session Scoped?: no + # """ + diff --git a/features/lock_lock.feature b/features/lock_lock.feature new file mode 100644 index 00000000..8a672f97 --- /dev/null +++ b/features/lock_lock.feature @@ -0,0 +1,14 @@ +Feature: Lock the node at a given path + In order to lock a node at a given path + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + And the "session_data.xml" fixtures are loaded + And the node at "/tests_general_base" has the mixin "mix:lockable" + + Scenario: Create a new lock + Given I execute the "lock:lock /tests_general_base --deep --session-scoped --timeout=30 --owner-info=random" command + Then the command should not fail + And the node "/tests_general_base" should be locked diff --git a/features/lock_refresh.feature b/features/lock_refresh.feature new file mode 100644 index 00000000..c15eb72d --- /dev/null +++ b/features/lock_refresh.feature @@ -0,0 +1,16 @@ +Feature: Refresh the TTL of a lock + In order to reset the TTL on the lock of a given node path + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + And I execute the "lock:lock /tests_general_base --session-scoped --timeout=10" command + + Scenario: Create a new node + And I execute the "lock:refresh /tests_general_base" command + Then the command should fail + And I should see the following: + """ + Not implemented + """ diff --git a/features/lock_token_add.feature b/features/lock_token_add.feature new file mode 100644 index 00000000..fc9e372e --- /dev/null +++ b/features/lock_token_add.feature @@ -0,0 +1,16 @@ +Feature: Add a lock token in the current session + In order to create a new lock token + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + + Scenario: Create a new node + Given I execute the "lock:token:add foobar" command + Then the command should fail + Then I should see the following: + """ + Not implemented + """ + diff --git a/features/lock_token_list.feature b/features/lock_token_list.feature new file mode 100644 index 00000000..b762ae05 --- /dev/null +++ b/features/lock_token_list.feature @@ -0,0 +1,14 @@ +Feature: List the lock tokens registered with the current session + In order to list the lock tokens registered in the current session + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + + Scenario: List lock tokens + Given I execute the "lock:token:add foobar" command + Then the command should fail + """ + Not implemented + """ diff --git a/features/lock_token_remove.feature b/features/lock_token_remove.feature new file mode 100644 index 00000000..5bc9770c --- /dev/null +++ b/features/lock_token_remove.feature @@ -0,0 +1,15 @@ +Feature: Remove a lock token in the current session + In order to create a new lock token + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + + Scenario: Create a new node + Given I execute the "lock:token:remove foobar" command + Then the command should fail + Then I should not see the following: + """ + Not implemented + """ diff --git a/features/lock_unlock.feature b/features/lock_unlock.feature new file mode 100644 index 00000000..917c80dc --- /dev/null +++ b/features/lock_unlock.feature @@ -0,0 +1,15 @@ +Feature: Unlock the node at a given path + In order to unlock a node at a given path + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + And the "session_data.xml" fixtures are loaded + And the node at "/tests_general_base" has the mixin "mix:lockable" + + Scenario: Create a new node + Given I execute the "lock:lock /tests_general_base --session-scoped" command + And I execute the "lock:unlock /tests_general_base" command + Then the command should not fail + And the node "/tests_general_base" should not be locked diff --git a/features/node_remove.feature b/features/node_remove.feature new file mode 100644 index 00000000..cfdd446c --- /dev/null +++ b/features/node_remove.feature @@ -0,0 +1,15 @@ +Feature: Remove a node + In order to remove a node + As a user that is logged into the shell + I should be able to run a command which does that + + Background: + Given that I am logged in as "testuser" + And the "session_data.xml" fixtures are loaded + + Scenario: Remove a node + Given the current node is "/tests_general_base" + And I execute the "node:remove" command + Then the command should not fail + And I save the session + And there should not exist a node at "/tests_general_base" diff --git a/src/PHPCR/Shell/Console/Application/ShellApplication.php b/src/PHPCR/Shell/Console/Application/ShellApplication.php index 937edad9..e7fa5205 100644 --- a/src/PHPCR/Shell/Console/Application/ShellApplication.php +++ b/src/PHPCR/Shell/Console/Application/ShellApplication.php @@ -83,6 +83,13 @@ use PHPCR\Shell\Console\Command\NodeSharedShowCommand; use PHPCR\Shell\Console\Command\NodeSharedRemoveCommand; use PHPCR\Shell\Console\Command\NodeRemoveCommand; +use PHPCR\Shell\Console\Command\LockLockCommand; +use PHPCR\Shell\Console\Command\LockInfoCommand; +use PHPCR\Shell\Console\Command\LockRefreshCommand; +use PHPCR\Shell\Console\Command\LockTokenAddCommand; +use PHPCR\Shell\Console\Command\LockTokenListCommand; +use PHPCR\Shell\Console\Command\LockTokenRemoveCommand; +use PHPCR\Shell\Console\Command\LockUnlockCommand; use Jackalope\NotImplementedException; use Symfony\Component\Console\Formatter\OutputFormatterStyle; @@ -181,6 +188,13 @@ public function init() $this->add(new NodeSharedShowCommand()); $this->add(new NodeSharedRemoveCommand()); $this->add(new NodeRemoveCommand()); + $this->add(new LockLockCommand()); + $this->add(new LockInfoCommand()); + $this->add(new LockRefreshCommand()); + $this->add(new LockTokenAddCommand()); + $this->add(new LockTokenListCommand()); + $this->add(new LockTokenRemoveCommand()); + $this->add(new LockUnlockCommand()); // add shell-specific commands $this->add(new ChangePathCommand()); diff --git a/src/PHPCR/Shell/Console/Command/LockInfoCommand.php b/src/PHPCR/Shell/Console/Command/LockInfoCommand.php new file mode 100644 index 00000000..f31b4c3f --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockInfoCommand.php @@ -0,0 +1,56 @@ +setName('lock:info'); + $this->setDescription('Create a node at the current path'); + $this->addArgument('absPath', InputArgument::REQUIRED, 'Absolute path of locked node'); + $this->setHelp(<<getHelper('phpcr')->getSession(); + $absPath = $input->getArgument('absPath'); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + + $lock = $lockManager->getLock($absPath); + + $info = array( + 'Lock owner' => $lock->getLockOwner(), + 'Lock token' => $lock->getLockToken(), + 'Seconds remaining' => $lock->getSecondsRemaining(), + 'Deep?' => $lock->isDeep() ? 'yes' : 'no', + 'Live?' => $lock->isLove() ? 'yes' : 'no', + 'Owned by current session?' => $lock->isLockOwningSession() ? 'yes' : 'no', + 'Session scoped?' => $lock->isSessionScoped() ? 'yes' : 'no', + ); + + $table = clone $this->getHelper('table'); + + foreach ($info as $label => $value) { + $table->addRow(array($label, $value)); + } + + $table->render($output); + } +} + diff --git a/src/PHPCR/Shell/Console/Command/LockLockCommand.php b/src/PHPCR/Shell/Console/Command/LockLockCommand.php new file mode 100644 index 00000000..931d792f --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockLockCommand.php @@ -0,0 +1,70 @@ +setName('lock:lock'); + $this->setDescription('Lock the node at the given path'); + $this->addArgument('absPath', InputArgument::REQUIRED, 'Absolute path of node to be locked'); + $this->addOption('deep', null, InputOption::VALUE_NONE, 'If given this lock will apply to this node and all its descendants; if not, it applies only to this node.'); + $this->addOption('session-scoped', null, InputOption::VALUE_NONE, 'If given, this lock expires with the current session; if not it expires when explicitly or automatically unlocked for some other reason'); + $this->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'Desired lock timeout in seconds (servers are free to ignore this value). If not used lock will not timeout'); + $this->addOption('owner-info', null, InputOption::VALUE_REQUIRED, ' string containing owner information supplied by the client; servers are free to ignore this value. If none is specified, the implementation chooses one (i.e. user name of current backend authentication credentials'); + $this->setHelp(<<absPath. + +If successful, the node is said to hold the lock. + +If deep option is given then the lock applies to the specified node and +all its descendant nodes; if false, the lock applies only to the +specified node. On a successful lock, the jcr:lockIsDeep property of the +locked node is set to this value. + +If session-scoped is specified then this lock will expire upon the +expiration of the current session (either through an automatic or +explicit sesiion:logout; if not given, this lock does not +expire until it is explicitly unlocked, it times out, or it is +automatically unlocked due to a implementation-specific limitation. + +The timeout parameter specifies the number of seconds until the +lock times out (if it is not refreshed with LockInterface::refresh() in +the meantime). An implementation may use this information as a hint or +ignore it altogether. Clients can discover the actual timeout by +inspecting the returned Lock object. + +The ownerInfo parameter can be used to pass a string holding +owner information relevant to the client. An implementation may either +use or ignore this parameter. + +The addition or change of the properties jcr:lockIsDeep and +jcr:lockOwnerare persisted immediately; there is no need to call save. + +It is possible to lock a node even if it is checked-in. +HERE + ); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $session = $this->getHelper('phpcr')->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + + $absPath = $input->getArgument('absPath'); + $isDeep = $input->getOption('deep'); + $isSessionScoped = $input->getOption('session-scoped'); + $timeout = $input->getOption('timeout'); + $ownerInfo = $input->getOption('owner-info'); + + $lockManager->lock($absPath, $isDeep, $isSessionScoped, $timeout, $ownerInfo); + } +} diff --git a/src/PHPCR/Shell/Console/Command/LockRefreshCommand.php b/src/PHPCR/Shell/Console/Command/LockRefreshCommand.php new file mode 100644 index 00000000..04ca8a90 --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockRefreshCommand.php @@ -0,0 +1,39 @@ +setName('lock:refresh'); + $this->setDescription('Refresh the TTL of the lock of the node at the given path'); + $this->addArgument('absPath', InputArgument::REQUIRED, 'Absolute path of node containing the lock to be refreshed'); + $this->setHelp(<<getHelper('phpcr')->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + + $absPath = $input->getArgument('absPath'); + + $lock = $lockManager->getLock($absPath); + $lock->refresh(); + } +} diff --git a/src/PHPCR/Shell/Console/Command/LockTokenAddCommand.php b/src/PHPCR/Shell/Console/Command/LockTokenAddCommand.php new file mode 100644 index 00000000..3b5e248b --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockTokenAddCommand.php @@ -0,0 +1,36 @@ +setName('lock:token:add'); + $this->setDescription('Add a lock token to the current session'); + $this->addArgument('lockToken', InputArgument::REQUIRED, 'Lock token'); + $this->setHelp(<<getHelper('phpcr')->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + $lockToken = $input->getArgument('lockToken'); + + $lockManager->addLockToken($lockToken); + } +} diff --git a/src/PHPCR/Shell/Console/Command/LockTokenListCommand.php b/src/PHPCR/Shell/Console/Command/LockTokenListCommand.php new file mode 100644 index 00000000..d7c04720 --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockTokenListCommand.php @@ -0,0 +1,44 @@ +setName('lock:token:list'); + $this->setDescription('List a lock token to the current session'); + $this->setHelp(<<getHelper('phpcr')->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + + $lockTokens = $lockManager->getLockTokens(); + + $table = clone $this->getHelper('table'); + $table->setHeaders(array('Token')); + + foreach ($lockTokens as $token) { + $table->addRow(array($token)); + } + + $table->render($output); + } +} diff --git a/src/PHPCR/Shell/Console/Command/LockTokenRemoveCommand.php b/src/PHPCR/Shell/Console/Command/LockTokenRemoveCommand.php new file mode 100644 index 00000000..f156205e --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockTokenRemoveCommand.php @@ -0,0 +1,33 @@ +setName('lock:token:remove'); + $this->setDescription('Remove a lock token to the current session'); + $this->addArgument('lockToken', InputArgument::REQUIRED, 'Lock token'); + $this->setHelp(<<getHelper('phpcr')->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + $lockToken = $input->getArgument('lockToken'); + + $lockManager->removeLockToken($lockToken); + } +} diff --git a/src/PHPCR/Shell/Console/Command/LockUnlockCommand.php b/src/PHPCR/Shell/Console/Command/LockUnlockCommand.php new file mode 100644 index 00000000..8b8351db --- /dev/null +++ b/src/PHPCR/Shell/Console/Command/LockUnlockCommand.php @@ -0,0 +1,50 @@ +setName('lock:unlock'); + $this->setDescription('Unlock the node at the given path'); + $this->addArgument('absPath', InputArgument::REQUIRED, 'Absolute path of node to be unlocked'); + $this->setHelp(<<Note: +However that the system may give permission to a non-owning session +to unlock a lock. Typically such "lock-superuser" capability is intended +to facilitate administrational clean-up of orphaned open-scoped locks. + +Note that it is possible to unlock a node even if it is checked-in (the +lock-related properties will be changed despite the checked-in status). +HERE + ); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $session = $this->getHelper('phpcr')->getSession(); + $workspace = $session->getWorkspace(); + $lockManager = $workspace->getLockManager(); + + $absPath = $input->getArgument('absPath'); + + $lockManager->unlock($absPath); + } +}