Page MenuHomePhorge

No OneTemporary

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

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)

Event Timeline