Phabricator Gantt Chart

Good Day,

Does Phabricator has feature or supported for Gantt Chart? or is there any method to implement Gantt Chart for my projects?

1 Like

No.

Actually there is this ugly workaround currently maintained by me.

It’s just a public Gantt landing page populated via some calls to the Conduit Phabricator APIs. Some features, so much limitations, but may fit your needs.

I don’t really suggest this approach. It should be better to build a Phabricator extension. But hey, it works.

The original idea comes from kennyenni.

Read also:

1 Like

Thank you Sir, I’ll try this your Gantt workaround later. Super Thanks again.

1 Like

Hi Sir,

I want to ask where I can put the source of your phabricatorGantt folder. I tried to upload here in /opt/bitnami/apps/phabricator/htdocs directory but when i enter the url with the foldername it says “404 not found”?

Thanks Sir.

@superpaulme To be honest I do not expose that page to the Internet. because it may exposes sensitive information.

In any case, if your Tasks are already public, you can do it in your way. It’s simple but you have to edit your webserver configuration. If you have Apache, edit your virtualhost configuration and adjust it for your needs. For example:

# add this line to avoid to rewrite this directory and just serve it as-is
RewriteRule ^/phabricatorGantt - [L]

RewriteRule ^(.*)$ /index.php?__path__=$1  [B,L,QSA]

Make sure to put that line before the already existing Phabricator rewrite rule. Then reload Apache.

P.S. Now you may want to enable the autocomplete, because with your solution you can do it. Change your config.php and set SHARE_PHABRICATOR_DOMAIN to true.

1 Like

I wrote an extension for Remarkup to support gantt chart and many other diagrams.
It use Kroki.io API to render diagrams from textual descriptions (support Mermaid, PlantUML, GraphViz, etc).

To create gantt chart with the extension, just write some remarkup text:

mermaid {{{

gantt
dateFormat  YYYY-MM-DD
title Adding GANTT diagram to mermaid
excludes weekdays 2014-01-10

section A section
Completed task            :done,    des1, 2014-01-06,2014-01-08
Active task               :active,  des2, 2014-01-09, 3d
Future task               :         des3, after des2, 5d
Future task2              :         des4, after des3, 5d

}}}

And then remarkup will render it to an image:

Extension source code:

<?php

/**
 * Remarkup Block Interpreter Extensions Collection
 *
 * File location: $phabricator_root/src/extensions/PhabricatorRemarkupBlockInterpreterExtentions.php
 *
 * @author: zengxs
 *
 * Remarkup Block Interpreter Collection
 *
 * Kroki Documentation: https://kroki.io/#how
 * Supported Diagrams:
 * - blockdiag
 * - seqdiag
 * - actdiag
 * - nwdiag
 * - packetdiag
 * - rackdiag
 * - c4plantuml
 * - ditaa
 * - erd
 * - graphviz
 * - mermaid
 * - nomnoml
 * - plantuml
 * - svgbob
 * - umlet
 * - vega
 * - vegalite
 * - wavedrom
 *
 */

abstract class PhabricatorRemarkupBaseBlockInterpreter extends PhutilRemarkupBlockInterpreter {
  protected static function parse_dimension($string) {
    $string = trim($string);
    return preg_match('/^(?:\d*\\.)?\d+%?$/', $string) ? $string : null;
  }

  protected static function base64_urlsafe_encode($string) {
    return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($string));
  }

  protected static function fetch_svg_image($url, $width) {
    $future = id(new HTTPSFuture($url))
      ->setMethod('GET')
      ->setTimeout(5);
    list($status, $body, $headers) = $future->resolve();

    $img = phutil_tag(
      'img',
      array(
        'src' => 'data:image/svg+xml;base64,' . base64_encode($body),
        'width' => nonempty($width, null),
      ));
    return phutil_tag(
      'div',
      array(
        'class' => 'phabricator-remarkup-embed-image-full',
        'style' => 'display: inline;',
      ),
      $img);
  }
}

abstract class PhabricatorRemarkupKrokiBlockInterpreter extends PhabricatorRemarkupBaseBlockInterpreter {
  protected static function fetch_kroki_image($content, $diagram_type, $width) {
    $encoded = self::base64_urlsafe_encode(zlib_encode($content, ZLIB_ENCODING_DEFLATE, 9));
    $url = "https://kroki.io/" . $diagram_type . "/svg/" . $encoded;
    return self::fetch_svg_image($url, $width);
  }

  public function markupContent($content, array $argv) {
    $width = self::parse_dimension(idx($argv, 'width'));

    return self::fetch_kroki_image($content, $this->getInterpreterName(), $width);
  }
}

final class PhabricatorRemarkupBlockdiagBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "blockdiag";
  }
}

final class PhabricatorRemarkupSeqdiagBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "seqdiag";
  }
}

final class PhabricatorRemarkupActdiagBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "actdiag";
  }
}

final class PhabricatorRemarkupNwdiagBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "nwdiag";
  }
}

final class PhabricatorRemarkupPacketdiagBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "packetdiag";
  }
}

final class PhabricatorRemarkupRackdiagBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "rackdiag";
  }
}

final class PhabricatorRemarkupC4PlantUMLBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "c4plantuml";
  }
}

final class PhabricatorRemarkupDitaaBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "ditaa";
  }
}

final class PhabricatorRemarkupErdBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "erd";
  }
}

final class PhabricatorRemarkupGraphvizBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "graphviz";
  }
}

final class PhabricatorRemarkupMermaidBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "mermaid";
  }
}

final class PhabricatorRemarkupNomnomlBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "nomnoml";
  }
}

final class PhabricatorRemarkupPlantUMLBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "plantuml";
  }
}

final class PhabricatorRemarkupSvgbobBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "svgbob";
  }
}

final class PhabricatorRemarkupUmletBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "umlet";
  }
}

final class PhabricatorRemarkupVegaBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "vega";
  }
}

final class PhabricatorRemarkupVegaLiteBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "vegalite";
  }
}

final class PhabricatorRemarkupWavedromBlockInterpreter extends PhabricatorRemarkupKrokiBlockInterpreter {
  public function getInterpreterName() {
    return "wavedrom";
  }
}

Hope this will be useful for you.

3 Likes

Thank you sir for the help, I manage to work your given source and boom it works. I will try explore other feature of DHTMLX for the phabricator gantt diagram. thanks sir.

Thanks for your extension.

Can i integrate in my github phabricator extension project and of course tag with your name, pseudo or something like that ?

Of course, you can.

I think this code should be public domain.

Added in extension : https://github.com/Supermarches-Match/phabricator-match-extensions

I just add configuration parameter to specify url for api