Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2895687
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
20 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.devcontainer/application/Dockerfile b/.devcontainer/application/Dockerfile
index 8f1db9f..f9744d1 100644
--- a/.devcontainer/application/Dockerfile
+++ b/.devcontainer/application/Dockerfile
@@ -1,64 +1,65 @@
FROM debian:bookworm-backports
# == Get ca-certificates up to date ==
RUN apt-get -y update
RUN apt-get -y install ca-certificates
# == Copy Data ==
COPY install_scripts /install_scripts
# == Configure Ubuntu ==
WORKDIR /install_scripts
RUN sh install_dependencies.sh
RUN sh add_users.sh
# == Set up the Phorge code base ==
RUN mkdir /srv/phorge
RUN chown git:wwwgrp-phorge /srv/phorge
USER git
WORKDIR /srv/phorge
RUN git clone https://we.phorge.it/source/phorge.git
RUN git clone https://we.phorge.it/source/arcanist.git
USER root
WORKDIR /
RUN mkdir -p /var/tmp/phd/log
RUN chown phorge-daemon:2000 /var/tmp/phd/log
# == Expose Ports ==
# Nginx
EXPOSE 80
# Aphlict
EXPOSE 22280
# SSH
EXPOSE 2222
# == Add service config files ==
ADD /config/nginx.conf.org /etc/nginx/
ADD /config/fastcgi.conf /etc/nginx/
ADD /config/php-fpm.conf /etc/php/8.2/fpm/
ADD /config/php.ini /etc/php/8.2/fpm/
+ADD /config/php.ini /etc/php/8.2/cli/
ADD /config/aphlict.phorge.json /install_scripts/
# == Add Supervisord config files ==
RUN mkdir -p /var/log/supervisor
RUN mkdir -p /etc/supervisor/conf.d/
ADD config/supervisord.conf /etc/supervisor/
COPY config/*.sv.conf /etc/supervisor/conf.d/
# == Configure Phorge SSH service ==
RUN mkdir /etc/phorge-ssh
RUN mkdir /var/run/sshd/
RUN chmod 0755 /var/run/sshd
ADD config/sshd_config.phorge /etc/phorge-ssh/
ADD config/phorge-ssh-hook.sh /etc/phorge-ssh/
RUN chown root:root /etc/phorge-ssh/*
# == Copy other scripts == #
COPY user-config /user-config
COPY startup.sh /
CMD bash ./startup.sh && supervisord
diff --git a/.devcontainer/application/setup.php b/.devcontainer/application/setup.php
index c1686ef..09c4f61 100644
--- a/.devcontainer/application/setup.php
+++ b/.devcontainer/application/setup.php
@@ -1,65 +1,68 @@
#!/usr/bin/env php
<?php
require_once './scripts/__init_script__.php';
-const query = new PhabricatorAuthProviderConfigQuery();
-$config = query
+$query = new PhabricatorAuthProviderConfigQuery();
+$config = $query
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIDs(array(1))
->executeOne();
if ($config) {
echo phutil_console_wrap("User/Password Auth Provider already configured\n");
return;
}
+echo phutil_console_wrap(
+ "Setting up dev environment with user admin/hunter2\n");
+
$password1 = new PhutilOpaqueEnvelope('hunter2');
$config = id(new PhabricatorAuthProviderConfig())
->setIsEnabled(1)
->setShouldAllowLogin(1)
->setShouldAllowRegistration(true)
->setShouldAllowLink(1)
->setShouldAllowUnlink(true)
->setProviderType('password')
->setProviderDomain('self')
->setProviderClass('PhabricatorPasswordAuthProvider')
->save();
$user = new PhabricatorUser();
$user->setUsername('admin');
$user->setRealName('admin');
$email = id(new PhabricatorUserEmail())
->setAddress('admin@example.com')
->setIsVerified(1);
$user->setIsApproved(1);
id(new PhabricatorUserEditor())
->setActor(PhabricatorUser::getOmnipotentUser())
->createNewUser($user, $email);
$xactions = array();
$xactions[] = id(new PhabricatorUserTransaction())
->setTransactionType(
PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE)
->setNewValue(true);
$source = id(new PhabricatorUnknownContentSource());
$actor = PhabricatorUser::getOmnipotentUser();
$people_application_phid = id(new PhabricatorPeopleApplication())
->getPHID();
$editor = id(new PhabricatorUserTransactionEditor())
->setActor($actor)
->setContentSource($source)
->setActingAsPHID($people_application_phid)
->setContinueOnMissingFields(true);
$editor->applyTransactions($user, $xactions);
$pass = PhabricatorAuthPassword::initializeNewPassword($user, PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT)
->setPassword($password1, $user)
->save();
diff --git a/.devcontainer/application/startup.sh b/.devcontainer/application/startup.sh
index fc2edc3..e45d1c8 100755
--- a/.devcontainer/application/startup.sh
+++ b/.devcontainer/application/startup.sh
@@ -1,59 +1,55 @@
#!/bin/bash
set -x
echo "Waiting for mysql"
until mysql -h"$SQL_HOST" -P"$SQL_PORT" -u"$SQL_USER" -p"$SQL_PASSWORD" &> /dev/null
do
printf "."
sleep 1
done
echo -e "\nmysql ready"
pushd /srv/phorge/phorge
./bin/config set mysql.host $SQL_HOST
./bin/config set mysql.port $SQL_PORT
./bin/config set mysql.user $SQL_USER
./bin/config set mysql.pass $SQL_PASSWORD
./bin/config set phabricator.base-uri $BASE_URI
./bin/config set phd.user phorge-daemon
./bin/config set diffusion.ssh-user git
./bin/config set diffusion.ssh-port 2222
./bin/config set diffusion.allow-http-auth true
./bin/config set phabricator.developer-mode true
./bin/config set phabricator.show-prototypes true
./bin/config set darkconsole.enabled true
./bin/config set storage.mysql-engine.max-size 268435456
./bin/config set pygments.enabled true
./bin/config set environment.append-paths '["/usr/lib/git-core"]'
./bin/config set notification.servers --stdin < /install_scripts/aphlict.phorge.json
echo '["/srv/phorge/deepclone/src"]' | ./bin/config set load-libraries --stdin
-php setup.php
-
-if [ -e /user-config/script.post ]; then
- echo "Applying post-configuration script..."
- /user-config/script.post
-fi
-
popd
pushd /srv/phorge/phorge/support/aphlict/server
npm ci
popd
-find /srv/phorge/deepclone
-
cp /etc/nginx/nginx.conf.org /etc/nginx/nginx.conf
-/srv/phorge/phorge/bin/storage upgrade --force
+
+pushd /srv/phorge/phorge
+
+./bin/storage upgrade --force
+
+php setup.php
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index ab8f92f..d0edbe2 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,22 +1,28 @@
<?php
/**
* This file is automatically generated. Use 'arc liberate' to rebuild it.
*
* @generated
* @phutil-library-version 2
*/
phutil_register_library_map(array(
'__library_version__' => 2,
'class' => array(
'DeepcloneApplication' => 'deepclone/application/DeepcloneApplication.php',
+ 'DeepcloneBaseTransaction' => 'xaction/DeepcloneBaseTransaction.php',
'DeepcloneController' => 'deepclone/controller/DeepcloneController.php',
+ 'DeepcloneSourceTransaction' => 'xaction/DeepcloneSourceTransaction.php',
+ 'DeepcloneTargetTransaction' => 'xaction/DeepcloneTargetTransaction.php',
'DeepcloneUIEventListener' => 'deepclone/events/DeepcloneUIEventListener.php',
),
'function' => array(),
'xmap' => array(
'DeepcloneApplication' => 'PhabricatorApplication',
+ 'DeepcloneBaseTransaction' => 'ManiphestTaskTransactionType',
'DeepcloneController' => 'ManiphestController',
+ 'DeepcloneSourceTransaction' => 'DeepcloneBaseTransaction',
+ 'DeepcloneTargetTransaction' => 'ManiphestTaskTransactionType',
'DeepcloneUIEventListener' => 'PhabricatorEventListener',
),
));
diff --git a/src/deepclone/controller/DeepcloneController.php b/src/deepclone/controller/DeepcloneController.php
index 2dc5555..6f35ce1 100644
--- a/src/deepclone/controller/DeepcloneController.php
+++ b/src/deepclone/controller/DeepcloneController.php
@@ -1,189 +1,236 @@
<?php
final class DeepcloneController extends ManiphestController
{
public function handleRequest(AphrontRequest $request)
{
$task_id = $request->getURIData('id');
$viewer = $this->getViewer();
if (!$task_id) {
throw new Exception("Task ID: $task_id");
return new Aphront404Response();
}
- $task = id(new ManiphestTaskQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($task_id))
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- )
- )
- ->needSubscriberPHIDs(true)
- ->executeOne();
+ $task = $this->getTask($task_id);
if (!$task) {
throw new Exception("Task ID: $task_id");
return new Aphront404Response();
}
+ $transactions = $this->getTransactions($task);
+
if ($request->isFormPost()) {
- return $this->triggerTransaction($request, $task);
- // $properties = $provider->readFormValuesFromRequest($request);
- // list($errors, $issues, $properties) = $provider->processEditForm(
- // $request,
- // $properties);
-
- // $xactions = array();
-
- // if (!$errors) {
- // if ($is_new) {
- // if (!phutil_nonempty_string($config->getProviderType())) {
- // $config->setProviderType($provider->getProviderType());
- // }
- // if (!phutil_nonempty_string($config->getProviderDomain())) {
- // $config->setProviderDomain($provider->getProviderDomain());
- // }
- // }
-
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_LOGIN)
- // ->setNewValue($request->getInt('allowLogin', 0));
-
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_REGISTRATION)
- // ->setNewValue($request->getInt('allowRegistration', 0));
-
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_LINK)
- // ->setNewValue($request->getInt('allowLink', 0));
-
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_UNLINK)
- // ->setNewValue($request->getInt('allowUnlink', 0));
-
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_TRUST_EMAILS)
- // ->setNewValue($request->getInt('trustEmails', 0));
-
- // if ($provider->supportsAutoLogin()) {
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_AUTO_LOGIN)
- // ->setNewValue($request->getInt('autoLogin', 0));
- // }
-
- // foreach ($properties as $key => $value) {
- // $xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
- // ->setTransactionType(
- // PhabricatorAuthProviderConfigTransaction::TYPE_PROPERTY)
- // ->setMetadataValue('auth:property', $key)
- // ->setNewValue($value);
- // }
-
- // if ($is_new) {
- // $config->save();
- // }
-
- // $editor = id(new PhabricatorAuthProviderConfigEditor())
- // ->setActor($viewer)
- // ->setContentSourceFromRequest($request)
- // ->setContinueOnNoEffect(true);
-
- // try {
- // $editor->applyTransactions($config, $xactions);
- // $next_uri = $config->getURI();
-
- // return id(new AphrontRedirectResponse())->setURI($next_uri);
- // } catch (Exception $ex) {
- // $validation_exception = $ex;
- // }
- // }
+ return $this->handleClone($request, $task, $transactions);
} else {
return $this->buildForm($task);
}
}
private function buildForm(ManiphestTask $task)
{
$form = id(new AphrontFormView())
->setUser($this->getViewer())
- ->appendChild(
- id(new AphrontFormCheckboxControl())
- ->setLabel(pht('Copy Comments'))
- ->addCheckbox(
- 'copy_comments',
- 1,
- pht('Copy all the comments of the task/s to the new task/s'),
- )
- )
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel('Deep Clone Task')
->addCheckbox(
'deep_clone',
1,
- 'Create a clone of all the sub tasks of this task',
- )
- );
+ pht('Create a clone of all the sub tasks of this task')));
$handle = $this->getHandle($task);
$cancel_uri = "/{$handle->getName()}";
$form
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue(pht('Clone'))
);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($this->generateTitle($task))
- // ->setFormErrors($errors)
- // ->setValidationException($validation_exception)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($this->generateTitle($task))
->appendChild($view);
}
private function generateTitle(ManiphestTask $task)
{
$handle = $this->getHandle($task);
- return pht('Deep clone task "%s" (%s)', $task->getTitle(), $handle->getName());
+ return pht('Clone task "%s" (%s)', $task->getTitle(), $handle->getName());
}
private function getHandle(ManiphestTask $task)
{
$viewer = $this->getViewer();
$handles = $viewer->loadHandles(array($task->getPHID()));
$handle = $handles[$task->getPHID()];
return $handle;
}
- private function triggerTransaction(AphrontRequest $request, ManiphestTask $task) {
- $deepCloneRaw = $request->getStr('deep_clone');
- $deepClone = $deepCloneRaw === '1';
- $copyCommentsRaw = $request->getStr('copy_comments');
- $copyComments = $copyCommentsRaw === '1';
+ private function getTask($task_id) {
+ $query = new ManiphestTaskQuery();
+
+ $task = $query
+ ->setViewer($this->getViewer())
+ ->withPHIDs(array($task_id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->needSubscriberPHIDs(true)
+ ->executeOne();
+
+ $field_list = PhabricatorCustomField::getObjectFields(
+ $task,
+ PhabricatorCustomField::ROLE_VIEW);
+ $field_list
+ ->setViewer($this->getViewer())
+ ->readFieldsFromStorage($task);
+
+ return $task;
+ }
+
+ private function getTransactions(ManiphestTask $task) {
+ $transactions = id(new ManiphestTransactionQuery())
+ ->setViewer($this->getViewer())
+ ->withObjectPHIDs(mpull([$task], 'getPHID'))
+ ->needComments(true)
+ ->execute();
+
+ return $transactions;
+ }
+
+ private function handleClone(
+ AphrontRequest $request,
+ ManiphestTask $task,
+ array $transactions) {
+ $deep_clone = $request->getStr('deep_clone') === '1';
+
+ $transaction_editor = id(new ManiphestTransactionEditor())
+ ->setActor($this->getViewer())
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ $task_clone = $this->cloneTask(
+ $task,
+ $transactions,
+ $deep_clone,
+ $transaction_editor);
+
+ return id(new AphrontRedirectResponse())->setURI($task_clone->getURI());
+ }
+
+
+ private function cloneTask(
+ ManiphestTask $task,
+ array $transactions,
+ $deep_clone,
+ $transaction_editor,
+ $parent = null) {
+ $task_clone = new ManiphestTask();
+
+ $task_clone->setTitle($task->getTitle());
+ $task_clone->setDescription($task->getDescription());
+ $task_clone->setAuthorPHID($task->getAuthorPHID());
+ $task_clone->setOwnerPHID($task->getOwnerPHID());
+ $task_clone->setStatus($task->getStatus());
+ $task_clone->setPriority($task->getPriority());
+ $task_clone->setSubtype($task->getSubtype());
+ $task_clone->setViewPolicy($task->getViewPolicy());
+ $task_clone->setEditPolicy($task->getEditPolicy());
+
+ $xactions = [];
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(PhabricatorCoreCreateTransaction::TRANSACTIONTYPE);
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(DeepcloneTargetTransaction::TRANSACTIONTYPE)
+ ->setNewValue($task->getPHID());
+
+
+ foreach ($transactions as &$transaction) {
+ if ($transaction->getTransactionType() !== 'core:customfield') {
+ continue;
+ }
+
+ $transaction_clone = id(new ManiphestTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD);
+
+ $transaction_clone->setMetadataValue(
+ 'customfield:key',
+ $transaction->getMetadataValue('customfield:key'));
+ $transaction_clone->setOldValue(null);
+ $transaction_clone->setNewValue($transaction->getNewValue());
+
+ $xactions[] = $transaction_clone;
+ }
+
+ if ($parent !== null) {
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(ManiphestTaskParentTransaction::TRANSACTIONTYPE)
+ ->setNewValue($parent->getPHID());
+ }
+
+ $transaction_editor->applyTransactions($task_clone, $xactions);
+
+ $xactions = [];
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(DeepcloneSourceTransaction::TRANSACTIONTYPE)
+ ->setNewValue($task_clone->getPHID());
+
+ $transaction_editor->applyTransactions($task, $xactions);
+
+ $task_clone->save();
+
+ if ($deep_clone === false) {
+ return $task_clone;
+ }
+
+ $task_graph = id(new ManiphestTaskGraph())
+ ->setViewer($this->getViewer())
+ ->setSeedPHID($task->getPHID())
+ ->setLimit(200)
+ ->loadGraph();
+
+ if ($task_graph->isEmpty()) {
+ return $task_clone;
+ }
+
+ $subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
+ $subtask_map = $task_graph->getEdges($subtask_type);
+
+ $subtask_list = idx($subtask_map, $task->getPHID(), array());
+
+ foreach ($subtask_list as &$subtask_phid) {
+ $sub_task = $this->getTask($subtask_phid);
+ $sub_transactions = $this->getTransactions($task);
+
+ $this->cloneTask(
+ $sub_task,
+ $sub_transactions,
+ $deep_clone,
+ $transaction_editor,
+ $task_clone);
+ }
- throw new Exception("Deep Clone $deepClone $deepCloneRaw Copy Comments $copyComments $copyCommentsRaw");
+ return $task_clone;
}
}
diff --git a/src/xaction/DeepcloneBaseTransaction.php b/src/xaction/DeepcloneBaseTransaction.php
new file mode 100644
index 0000000..93936ac
--- /dev/null
+++ b/src/xaction/DeepcloneBaseTransaction.php
@@ -0,0 +1,24 @@
+<?php
+
+abstract class DeepcloneBaseTransaction
+ extends ManiphestTaskTransactionType {
+ public function generateOldValue($object) {
+ return 'donthide';
+ }
+
+ public function getActionName() {
+ return pht('Cloned');
+ }
+
+ public function getIcon() {
+ return 'fa-clone';
+ }
+
+ public function shouldHideForFeed() {
+ return false;
+ }
+
+ public function shouldHide() {
+ return false;
+ }
+}
diff --git a/src/xaction/DeepcloneSourceTransaction.php b/src/xaction/DeepcloneSourceTransaction.php
new file mode 100644
index 0000000..a387a2c
--- /dev/null
+++ b/src/xaction/DeepcloneSourceTransaction.php
@@ -0,0 +1,21 @@
+<?php
+
+final class DeepcloneSourceTransaction
+ extends DeepcloneBaseTransaction {
+ const TRANSACTIONTYPE = 'clone_source';
+
+ public function getTitle() {
+ $source = $this->getNewValue();
+ $target = $this->getObject();
+
+ return pht(
+ '%s cloned %s to %s',
+ $this->renderAuthor(),
+ $this->renderHandle($target->getPHID()),
+ $this->renderHandle($source));
+ }
+
+ public function getTitleForFeed() {
+ return $this->getTitle();
+ }
+}
diff --git a/src/xaction/DeepcloneTargetTransaction.php b/src/xaction/DeepcloneTargetTransaction.php
new file mode 100644
index 0000000..9dcbbe8
--- /dev/null
+++ b/src/xaction/DeepcloneTargetTransaction.php
@@ -0,0 +1,21 @@
+<?php
+
+final class DeepcloneTargetTransaction
+ extends DeepcloneBaseTransaction {
+ const TRANSACTIONTYPE = 'clone_target';
+
+ public function getTitle() {
+ $source = $this->getNewValue();
+ $target = $this->getObject();
+
+ return pht(
+ '%s cloned %s to %s',
+ $this->renderAuthor(),
+ $this->renderHandle($source),
+ $this->renderHandle($target->getPHID()));
+ }
+
+ public function getTitleForFeed() {
+ return $this->getTitle();
+ }
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jan 19 2025, 21:53 (6 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1129133
Default Alt Text
(20 KB)
Attached To
Mode
R13 DeepClone
Attached
Detach File
Event Timeline
Log In to Comment