Page MenuHomePhorge

D25698.1734691381.diff
No OneTemporary

D25698.1734691381.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -436,6 +436,7 @@
'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php',
'CountdownEditConduitAPIMethod' => 'applications/countdown/conduit/CountdownEditConduitAPIMethod.php',
'CountdownSearchConduitAPIMethod' => 'applications/countdown/conduit/CountdownSearchConduitAPIMethod.php',
+ 'CowsayReferenceController' => 'applications/reference/src/controller/CowsayReferenceController.php',
'DarkConsoleController' => 'applications/console/controller/DarkConsoleController.php',
'DarkConsoleCore' => 'applications/console/core/DarkConsoleCore.php',
'DarkConsoleDataController' => 'applications/console/controller/DarkConsoleDataController.php',
@@ -1304,6 +1305,7 @@
'FeedStoryNotificationGarbageCollector' => 'applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php',
'FerretConfigurableSearchFunction' => 'applications/search/ferret/function/FerretConfigurableSearchFunction.php',
'FerretSearchFunction' => 'applications/search/ferret/function/FerretSearchFunction.php',
+ 'FigletReferenceController' => 'applications/reference/src/controller/FigletReferenceController.php',
'FileAllocateConduitAPIMethod' => 'applications/files/conduit/FileAllocateConduitAPIMethod.php',
'FileConduitAPIMethod' => 'applications/files/conduit/FileConduitAPIMethod.php',
'FileCreateMailReceiver' => 'applications/files/mail/FileCreateMailReceiver.php',
@@ -5878,7 +5880,11 @@
'ProjectSearchConduitAPIMethod' => 'applications/project/conduit/ProjectSearchConduitAPIMethod.php',
'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php',
'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php',
+ 'ReferenceApplication' => 'applications/reference/src/application/ReferenceApplication.php',
+ 'ReferenceController' => 'applications/reference/src/controller/ReferenceController.php',
'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php',
+ 'RemarkupReferenceController' => 'applications/reference/src/controller/RemarkupReferenceController.php',
+ 'RemarkupSyntaxDocumentationProvider' => 'infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php',
'RemarkupValue' => 'applications/remarkup/RemarkupValue.php',
'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php',
'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php',
@@ -6444,6 +6450,7 @@
'ConpherenceViewController' => 'ConpherenceController',
'CountdownEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'CountdownSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
+ 'CowsayReferenceController' => 'ReferenceController',
'DarkConsoleController' => 'PhabricatorController',
'DarkConsoleCore' => 'Phobject',
'DarkConsoleDataController' => 'PhabricatorController',
@@ -7404,6 +7411,7 @@
'FeedStoryNotificationGarbageCollector' => 'PhabricatorGarbageCollector',
'FerretConfigurableSearchFunction' => 'FerretSearchFunction',
'FerretSearchFunction' => 'Phobject',
+ 'FigletReferenceController' => 'ReferenceController',
'FileAllocateConduitAPIMethod' => 'FileConduitAPIMethod',
'FileConduitAPIMethod' => 'ConduitAPIMethod',
'FileCreateMailReceiver' => 'PhabricatorApplicationMailReceiver',
@@ -11211,12 +11219,18 @@
'PhabricatorRegistrationProfile' => 'Phobject',
'PhabricatorRemarkupCachePurger' => 'PhabricatorCachePurger',
'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl',
- 'PhabricatorRemarkupCowsayBlockInterpreter' => 'PhutilRemarkupBlockInterpreter',
+ 'PhabricatorRemarkupCowsayBlockInterpreter' => array(
+ 'PhutilRemarkupBlockInterpreter',
+ 'RemarkupSyntaxDocumentationProvider',
+ ),
'PhabricatorRemarkupCustomBlockRule' => 'PhutilRemarkupBlockRule',
'PhabricatorRemarkupCustomInlineRule' => 'PhutilRemarkupRule',
'PhabricatorRemarkupDocumentEngine' => 'PhabricatorDocumentEngine',
'PhabricatorRemarkupEditField' => 'PhabricatorEditField',
- 'PhabricatorRemarkupFigletBlockInterpreter' => 'PhutilRemarkupBlockInterpreter',
+ 'PhabricatorRemarkupFigletBlockInterpreter' => array(
+ 'PhutilRemarkupBlockInterpreter',
+ 'RemarkupSyntaxDocumentationProvider',
+ ),
'PhabricatorRemarkupHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension',
'PhabricatorRemarkupUIExample' => 'PhabricatorUIExample',
'PhabricatorRemoveEmailUserLogType' => 'PhabricatorUserLogType',
@@ -12793,7 +12807,10 @@
'ProjectSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'QueryFormattingTestCase' => 'PhabricatorTestCase',
'QueryFuture' => 'Future',
+ 'ReferenceApplication' => 'PhabricatorApplication',
+ 'ReferenceController' => 'PhabricatorController',
'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod',
+ 'RemarkupReferenceController' => 'ReferenceController',
'RemarkupValue' => 'Phobject',
'RepositoryConduitAPIMethod' => 'ConduitAPIMethod',
'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod',
diff --git a/src/applications/reference/src/application/ReferenceApplication.php b/src/applications/reference/src/application/ReferenceApplication.php
new file mode 100644
--- /dev/null
+++ b/src/applications/reference/src/application/ReferenceApplication.php
@@ -0,0 +1,27 @@
+<?php
+
+final class ReferenceApplication extends PhabricatorApplication {
+
+ public function getName() {
+ return pht('Reference');
+ }
+
+ public function getIcon() {
+ return 'fa-code';
+ }
+
+ public function isUnlisted() {
+ return true;
+ }
+
+ public function getRoutes() {
+ return array(
+ '/reference/' => array(
+ 'remarkup/' => 'RemarkupReferenceController',
+ 'cowsay/' => 'CowsayReferenceController',
+ 'figlet/' => 'FigletReferenceController',
+ ),
+ );
+ }
+
+}
diff --git a/src/applications/reference/src/controller/CowsayReferenceController.php b/src/applications/reference/src/controller/CowsayReferenceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/reference/src/controller/CowsayReferenceController.php
@@ -0,0 +1,298 @@
+<?php
+
+final class CowsayReferenceController extends ReferenceController {
+ private function getJokes() {
+ $jokes = [];
+ $jokes[] =
+ $jokes[] = 'Why did the team leader cross the road? To get to the'.
+ ' other side and avoid responsibility.';
+ $jokes[] = "What do you call a mathematician who's afraid of".
+ ' negative numbers? A coward.';
+ $jokes[] = 'Why did the sales person cross the road? To sell the'.
+ ' chicken a new coop.';
+ $jokes[] = 'I told my doctor that I broke my arm in two places. He'.
+ ' told me to stop going to those places.';
+ $jokes[] = 'Why do managers always carry a briefcase? So they have'.
+ " something heavy to throw at their employees when they're not".
+ ' meeting their targets.';
+ $jokes[] = 'What do you call a sales person with a conscience?'.
+ ' Unemployed.';
+ $jokes[] = 'Why did the biologist bring a microscope to the party?'.
+ ' Because she wanted to see things up close and personal.';
+ $jokes[] = "Why do IT guys like to work in the dark? Because it's".
+ ' easier to hide their mistakes';
+ $jokes[] = 'How do you recognize a happy motorcyclist? By the bugs'.
+ ' in his teeth.';
+ $jokes[] = 'Why did the tomato turn red? Because it saw the salad'.
+ ' dressing.';
+ $jokes[] = 'Why did the engineer go to the beach? To build sand'.
+ ' castles, of course.';
+ $jokes[] = "Why don't sales reps use trampolines? They never bounce".
+ ' back from rejection.';
+ $jokes[] = "Why don't sales people play hide and seek? Because".
+ ' nobody would look for them.';
+ $jokes[] = 'Why did the lumberjack break up with his girlfriend? She'.
+ " couldn't handle his wood.";
+ $jokes[] = 'I once went to a meeting that was so pointless, I'.
+ ' started questioning the meaning of life...';
+ $jokes[] = 'How many sales people does it take to change a'.
+ " lightbulb? None, they'll convince you the old one is still working".
+ ' fine.';
+ $jokes[] = "What do you call an AI that can't learn from its".
+ ' mistakes? A broken record.';
+ $jokes[] = 'Why did the engineer bring a ruler to bed? He wanted to'.
+ ' see how long he could sleep.';
+ $jokes[] = "What did the sales rep say to the customer who couldn't".
+ " afford the product? Don't worry, it's a steal!";
+ $jokes[] = "I just burned 2000 calories. That's the last time I".
+ ' leave brownies in the oven while I nap.';
+ $jokes[] = 'Why was 6 afraid of 7? Because 7 8 9.';
+ $jokes[] = 'Why did the coffee file a police report? It got mugged.';
+ $jokes[] = 'Why did the plumber go to the opera? He wanted to see a'.
+ ' show about pipe organs.';
+ $jokes[] = 'How many HR people does it take to change a light bulb?'.
+ " None, they're too busy writing a policy on it.";
+ $jokes[] = "What did the coffee say to the tea? You're too weak".
+ ' for my taste.';
+ $jokes[] = 'Programmers are a unique breed of individuals who have'.
+ ' the power to turn coffee into code.';
+ $jokes[] = 'What do you call an opera singer who can also fix a'.
+ ' car? A mechanic.';
+ $jokes[] = "Dog owners are really the only people who think it's".
+ " normal to pick up another species' poop.";
+ $jokes[] = 'Why do some people bring a notebook to meetings? So'.
+ " they can write down all the reasons they're never going to do any".
+ ' of the things discussed in the meeting.';
+ $jokes[] = 'Why did the project manager go to the beach? To check'.
+ ' if the tide was on schedule.';
+ $jokes[] = 'How many Nobel laureates does it take to change a'.
+ ' lightbulb? Two. One to hold the lightbulb and the other to'.
+ ' rotate the universe.';
+ $jokes[] = 'Why did the chemist bring a calculator to the party?'.
+ ' Because she wanted to find the solution to every problem.';
+ $jokes[] = 'Why did the cyclist bring a ladder to the race? He'.
+ ' wanted to get to the top of the podium.';
+ $jokes[] = "Why don't eggs tell jokes? They'd crack each other up.";
+ $jokes[] = 'Why was the math book sad? It had too many problems.';
+ $jokes[] = "Trust me, this is a once-in-a-lifetime opportunity.
+ It's like a unicorn, but with more broken promises.";
+ $jokes[] = 'Why do programmers prefer dark mode? Because'.
+ ' light attracts bugs.';
+ $jokes[] = "How many women does it take to change a lightbulb?'.
+ ' None, they just sit in the dark and bitch about how they".
+ " can't see anything!";
+ $jokes[] = "You won't find a better deal anywhere else, unless,".
+ ' of course, you look on the internet.';
+ $jokes[] = 'How do you know if a programmer is extroverted?'.
+ ' They stare at your shoes when they talk to you.';
+ $jokes[] = "\"I'm sorry\" and \"I apologize\" mean the same".
+ " thing. Unless you're at a funeral.";
+ $jokes[] = 'A man walks into a bar...ouch';
+ $jokes[] = 'Why do managers always have their hands in their'.
+ " pockets? Because they're trying to find the key to their".
+ " employees' motivation.";
+ $jokes[] = "What do you call a brat who doesn't believe in".
+ ' Santa? A rebel without a Claus.';
+ $jokes[] = 'How many programmers does it take to change a light'.
+ " bulb? None, that's a hardware issue.";
+ $jokes[] = 'How does NASA organize a party? They planet.';
+ $jokes[] = "I'm reading a book on anti-gravity. It's impossible".
+ ' to put down.';
+ $jokes[] = "Why don't mathematicians get married? They'd always".
+ ' be looking for a better half.';
+ $jokes[] = "What's an electrical engineer's favorite type of".
+ ' exercise? Circuit training.';
+ $jokes[] = 'How does a CEO prepare for a presentation? By'.
+ ' practicing his power-point-of-view.';
+ $jokes[] = 'Why did the salesman bring a ladder to the sales'.
+ ' meeting? To reach his quota.';
+ $jokes[] = 'The special offer is like a regular offer, but'.
+ ' with more glitter and less substance.';
+ $jokes[] = 'How does a project manager propose to his partner?'.
+ ' \"Will you accept this project to marry me?\"';
+ $jokes[] = "Why did the programmer quit his job? He didn't".
+ ' get arrays.';
+ $jokes[] = 'Why did the chemist take a piece of chalk to bed?'.
+ ' To draw a conclusion.';
+ $jokes[] = "What's the difference between a sales person and a".
+ ' mosquito? A mosquito stops sucking when you slap it.';
+ $jokes[] = "Why don't scientists trust atoms? Because they".
+ ' make up everything.';
+ $jokes[] = 'Why do women have small feet? So they can stand'.
+ ' closer to the kitchen sink.';
+ $jokes[] = 'I just ordered a chicken and an egg from Amazon.'.
+ " I'll let you know.";
+ $jokes[] = "Why don't help desk technicians go on vacation?".
+ " Because they're afraid the users will break something while".
+ " they're gone.";
+ $jokes[] = "I'm on a seafood diet. I see food, I eat it.";
+ $jokes[] = "What's the difference between a motorcycle and a".
+ ' vacuum cleaner? The location of the dirtbag.';
+ $jokes[] = 'Why did the two 4s skip lunch? They already 8.';
+ $jokes[] = "What do you call a programmer who doesn't comment".
+ ' their code? A future team leader.';
+ $jokes[] = 'Why did the cookie go to the doctor? Because he was'.
+ ' feeling crumby.';
+ $jokes[] = "What's the difference between a poorly dressed man on".
+ ' a unicycle and a well-dressed man on a bicycle? Attire.';
+ $jokes[] = 'Why did the statistician always carry a ruler to the'.
+ ' party? He wanted to measure the standard deviation of the fun.';
+ $jokes[] = 'Why did the math teacher go to the beach? He wanted'.
+ ' to work on his tan-gent.';
+ $jokes[] = 'What do you call a factory that makes okay products?'.
+ ' A satisfactory.';
+ $jokes[] = 'Why did the philosopher refuse to buy a new calendar?'.
+ ' He wanted to wait until the end of time.';
+ $jokes[] = 'Why do civil engineers refuse to play hide and seek?'.
+ ' They always leave concrete evidence.';
+ $jokes[] = 'I told the help desk technician that my computer was'.
+ " running slow, and he said, \"That's because it's trying to keep".
+ ' up with your brain.\"';
+ $jokes[] = "What's the difference between a sales person and a".
+ ' magician? A magician knows when to disappear.';
+ $jokes[] = 'Why did the sales rep sit on the photocopier? He wanted'.
+ ' to make a good impression.';
+ $jokes[] = 'Why do scuba divers fall backwards off the boat?'.
+ ' Because if they fell forward, they would still be in the boat.';
+ $jokes[] = "Why do some people love meetings? Because it's the".
+ ' only time they can catch up on their sleep without getting fired.';
+ $jokes[] = 'What do you call fake spaghetti? An impasta.';
+ $jokes[] = 'Why did the electrical engineer become a comedian?'.
+ ' They wanted to make light of the situation.';
+ $jokes[] = "Why do men like to play video games? Because it's the".
+ ' only way they can win.';
+ $jokes[] = 'Why did the graduate cross the road? To prove to their'.
+ " parents that they could make it on their own... even if it's just".
+ ' to the other side of the street.';
+ $jokes[] = 'Why did the manufacturing operator get fired? He'.
+ " couldn't even screw up the right way.";
+ $jokes[] = "Why don't politicians campaign in cemeteries? Because".
+ " they're afraid of waking the voters.";
+ $jokes[] = 'What do you call a programmer who never makes'.
+ ' mistakes? A liar.';
+ $jokes[] = 'Why did the manager go to the psychologist? Because'.
+ ' he wanted to learn how to delegate his problems to someone else.';
+ $jokes[] = 'I sent my wife to the kitchen to make me a sandwich,'.
+ " but she couldn't find it.";
+ $jokes[] = "What rock group has four men who don't sing? Mount".
+ ' Rushmore.';
+ $jokes[] = 'Why did the motorcycle go to the doctor? It was two'.
+ ' tired.';
+ $jokes[] = "Why don't project managers make good cooks? They".
+ ' always follow the recipe too closely and never improvise.';
+ $jokes[] = 'I told my wife she was drawing her eyebrows too high.'.
+ ' She looked surprised.';
+ $jokes[] = 'If you think hunting is expensive, think of all the'.
+ " money you'll save when your friends stop inviting you out to".
+ ' dinner.';
+ $jokes[] = 'Why do meetings always start late? Because the boss'.
+ " is too busy practicing his \"I'm important\" face in the mirror.";
+ $jokes[] = "I've heard that smoking is a dying habit, but I".
+ " didn't realize it was so literal.";
+ $jokes[] = "I asked God for a bike, but I know God doesn't work".
+ ' that way. So I stole a bike and asked for forgiveness.';
+ $jokes[] = "What do you call a project manager who doesn't believe".
+ ' in deadlines? A myth.';
+ $jokes[] = 'Why do meetings feel like they last forever? Because'.
+ " time flies when you're having fun.";
+ $jokes[] = 'Why did the HR person become a detective? They'.
+ ' wanted to solve the mystery of the missing stapler.';
+ $jokes[] = 'The IT guy at work seemingly vanished today.'.
+ ' He ransomware.';
+ $jokes[] = 'My imaginary friend thinks I have problems.';
+ $jokes[] = "Why don't climate activists go on vacation? Because".
+ " they're afraid the ice caps will melt while they're gone.";
+ $jokes[] = 'Why do sales people wear slip-on shoes? You'.
+ " don't need to tie something up if you're going to run away with it.";
+
+ return $jokes;
+ }
+
+ public function getTitle() {
+ return 'Cowsay reference';
+ }
+
+ public function getContent() {
+ $content = <<<EOTEXT
+= Cowsay reference
+== Templates
+EOTEXT;
+
+ $root = dirname(phutil_get_library_root('phorge'));
+
+ $directories = array(
+ $root.'/externals/cowsay/cows/',
+ $root.'/resources/cows/builtin/',
+ $root.'/resources/cows/custom/',
+ );
+
+ // randomize jokes sequence
+ $jokes = $this->getJokes();
+ $indices = range(0, count($jokes) - 1);
+ for ($i = count($indices) - 1; $i > 0; $i--) {
+ $random_index = rand(0, $i);
+ $temp = $indices[$i];
+ $indices[$i] = $indices[$random_index];
+ $indices[$random_index] = $temp;
+ }
+
+ $j = 0;
+
+ foreach ($directories as $directory) {
+ foreach (Filesystem::listDirectory($directory, false) as $cow_file) {
+ $matches = null;
+ if (!preg_match('/^(.*)\.cow$/', $cow_file, $matches)) {
+ continue;
+ }
+ $cow_name = $matches[1];
+ $cow_name = phutil_utf8_strtolower($cow_name);
+
+ $joke = $jokes[$indices[$j] % count($jokes)];
+
+ $content .= "\n=== ".$cow_name;
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='".$cow_name."'){{{{$joke}}}}";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='".$cow_name."'){{{{$joke}}}}";
+ $content .= "\n";
+
+ $j++;
+ }
+ }
+
+ $content .= "\n";
+ $content .= '== Parameters';
+
+ $joke = $jokes[$indices[$j] % count($jokes)];
+ $j++;
+
+ $content .= "\n=== eyes";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='cow', eyes='-x'){{{{$joke}}}}";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='cow', eyes='-x'){{{{$joke}}}}";
+ $content .= "\n";
+
+ $joke = $jokes[$indices[$j] % count($jokes)];
+ $j++;
+
+ $content .= "\n=== think";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='cow', think=1){{{{$joke}}}}";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='cow', think=1){{{{$joke}}}}";
+ $content .= "\n";
+
+ $joke = $jokes[$indices[$j] % count($jokes)];
+ $j++;
+
+ $content .= "\n=== tongue";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='cow', tongue=U){{{{$joke}}}}";
+ $content .= "\n```";
+ $content .= "\ncowsay(cow='cow', tongue=U){{{{$joke}}}}";
+ $content .= "\n";
+
+ return $content;
+ }
+}
diff --git a/src/applications/reference/src/controller/FigletReferenceController.php b/src/applications/reference/src/controller/FigletReferenceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/reference/src/controller/FigletReferenceController.php
@@ -0,0 +1,39 @@
+<?php
+
+final class FigletReferenceController extends ReferenceController {
+ public function getTitle() {
+ return 'Figlet reference';
+ }
+
+ public function getContent() {
+ $content = <<<EOTEXT
+= Figlet reference
+== Fonts
+EOTEXT;
+
+ $root = dirname(phutil_get_library_root('phorge'));
+
+ $dirs = array(
+ $root.'/externals/figlet/fonts/',
+ $root.'/externals/pear-figlet/fonts/',
+ $root.'/resources/figlet/custom/',
+ );
+
+ foreach ($dirs as $dir) {
+ foreach (Filesystem::listDirectory($dir, false) as $file) {
+ if (preg_match('/\.flf$/', $file)) {
+ $name = phutil_utf8_strtolower($file);
+ $name = preg_replace('/\.flf$/', '', $name);
+ $content .= "\n=== ".$name;
+ $content .= "\n```";
+ $content .= "\nfiglet (font=".$name."){{{Great work!}}}";
+ $content .= "\n```";
+ $content .= "\nfiglet (font=".$name."){{{Great work!}}}";
+ $content .= "\n";
+ }
+ }
+ }
+
+ return $content;
+ }
+}
diff --git a/src/applications/reference/src/controller/ReferenceController.php b/src/applications/reference/src/controller/ReferenceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/reference/src/controller/ReferenceController.php
@@ -0,0 +1,75 @@
+<?php
+
+abstract class ReferenceController extends PhabricatorController {
+
+ abstract public function getContent();
+ abstract public function getTitle();
+
+ public function handleRequest(AphrontRequest $request) {
+ $request = $this->getRequest();
+ $viewer = $request->getViewer();
+
+ $content = $this->getContent();
+
+ $remarkup_view = id(new PHUIRemarkupView($viewer, $content))
+ ->setContextObject($this)
+ ->setRemarkupOption('uri.same-window', true)
+ ->setRemarkupOption(PHUIRemarkupView::OPTION_GENERATE_TOC, true)
+ ->setRemarkupOption(PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS, false)
+ ->setGenerateTableOfContents(true);
+
+ $content = $remarkup_view->render();
+
+ $toc = $remarkup_view->getTableOfContents();
+ $toc = $this->formatToc($toc);
+
+ $document = id(new PHUIDocumentView())
+ ->addClass('reference-documentation')
+ ->setToc($toc)
+ ->appendChild($remarkup_view);
+
+ $crumbs = $this->buildApplicationCrumbs();
+
+ return $this->newPage()
+ ->setTitle($this->getTitle())
+ ->setCrumbs($crumbs)
+ ->appendChild($document);
+ }
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = array();
+
+ $application = $this->getCurrentApplication();
+ if ($application) {
+ $icon = $application->getIcon();
+ if (!$icon) {
+ $icon = 'fa-puzzle';
+ }
+
+ $crumbs[] = id(new PHUICrumbView())
+ ->setName($this->getTitle())
+ ->setIcon($icon);
+ }
+
+ $view = new PHUICrumbsView();
+ foreach ($crumbs as $crumb) {
+ $view->addCrumb($crumb);
+ }
+
+ return $view;
+ }
+
+ protected function formatToc($toc) {
+
+ if ($toc) {
+ $toc = phutil_tag_div('phui-document-toc-content', array(
+ phutil_tag_div(
+ 'phui-document-toc-header',
+ pht('Contents')),
+ $toc,
+ ));
+ }
+
+ return $toc;
+ }
+}
diff --git a/src/applications/reference/src/controller/RemarkupReferenceController.php b/src/applications/reference/src/controller/RemarkupReferenceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/reference/src/controller/RemarkupReferenceController.php
@@ -0,0 +1,762 @@
+<?php
+
+final class RemarkupReferenceController extends ReferenceController {
+ public function getTitle() {
+ return 'Remarkup reference';
+ }
+
+ public function getContent() {
+ $content = <<<EOTEXT
+= Remarkup Reference
+Explains how to make bold text; this makes your words louder so you can win
+arguments.
+
+= Overview =
+
+Phorge uses a lightweight markup language called "Remarkup", similar to
+other lightweight markup languages like Markdown and Wiki markup.
+
+This document describes how to format text using Remarkup.
+
+= Quick Reference =
+
+All the syntax is explained in more detail below, but this is a quick guide to
+formatting text in Remarkup.
+
+These are inline styles, and can be applied to most text:
+
+ **bold** //italic// `monospaced` ##monospaced## ~~deleted~~ __underlined__
+ !!highlighted!!
+ D123 T123 rX123 # Link to Objects
+ {D123} {T123} # Link to Objects (Full Name)
+ {F123} # Embed Images
+ {M123} # Embed Pholio Mock
+ @username # Mention a User
+ #project # Mention a Project
+ [[wiki page]] # Link to Phriction
+ [[wiki page | name]] # Named link to Phriction
+ http://xyz/ # Link to web
+ [[http://xyz/ | name]] # Named link to web
+ [name](http://xyz/) # Alternate Link
+
+These are block styles, and must be separated from surrounding text by
+empty lines:
+
+ = Large Header =
+
+ == Smaller Header ==
+
+ ## This is a Header As Well
+
+ Also a Large Header
+ ===================
+
+ Also a Smaller Header
+ ---------------------
+
+ > Quoted Text
+
+ Use `- ` or `* ` for bulleted lists, and `# ` for numbered lists.
+ Use ``` or indent two spaces for code.
+ Use %%% for a literal block.
+ Use | ... | ... for tables.
+
+= Basic Styling =
+
+Format **basic text styles** like this:
+
+ **bold text**
+ //italic text//
+ `monospaced text`
+ ##monospaced text##
+ ~~deleted text~~
+ __underlined text__
+ !!highlighted text!!
+
+Those produce **bold text**, //italic text//, `monospaced text`, ##monospaced
+text##, ~~deleted text~~, __underlined text__, and !!highlighted text!!
+respectively.
+
+= Layout =
+
+Make **headers** like this:
+
+ = Large Header =
+
+ == Smaller Header ==
+
+ ===== Very Small Header =====
+
+ Alternate Large Header
+ ======================
+
+ Alternate Smaller Header
+ ------------------------
+
+You can optionally omit the trailing `=` signs -- that is, these are the same:
+
+ == Smaller Header ==
+
+ == Smaller Header
+
+This produces headers like the ones in this document. Make sure you have an
+empty line before and after the header.
+
+Lists
+=====
+
+Make **lists** by beginning each item with a `-` or a `*`:
+
+ lang=text
+ - milk
+ - eggs
+ - bread
+
+ * duck
+ * duck
+ * goose
+
+This produces a list like this:
+
+ - milk
+ - eggs
+ - bread
+
+(Note that you need to put a space after the `-` or `*`.)
+
+You can make numbered lists with a `#` instead of `-` or `*`:
+
+ # Articuno
+ # Zapdos
+ # Moltres
+
+Numbered lists can also be started with `1.` or `1)`. If you use a number other
+than `1`, the list will start at that number instead. For example, this:
+
+```
+ 200) OK
+ 201) Created
+ 202) Accepted
+```
+
+...produces this:
+
+ 200) OK
+ 201) Created
+ 202) Accepted
+
+You can also nest lists:
+
+```- Body
+ - Head
+ - Arm
+ - Elbow
+ - Hand
+ # Thumb
+ # Index
+ # Middle
+ # Ring
+ # Pinkie
+ - Leg
+ - Knee
+ - Foot```
+
+...which produces:
+
+ - Body
+ - Head
+ - Arm
+ - Elbow
+ - Hand
+ # Thumb
+ # Index
+ # Middle
+ # Ring
+ # Pinkie
+ - Leg
+ - Knee
+ - Foot
+
+If you prefer, you can indent lists using multiple characters to show indent
+depth, like this:
+
+```- Tree
+-- Branch
+--- Twig```
+
+As expected, this produces:
+
+- Tree
+-- Branch
+--- Twig
+
+You can add checkboxes to items by prefacing them with `[ ]` or `[X]`, like
+this:
+
+```
+ - [X] Preheat oven to 450 degrees.
+ - [ ] Zest 35 lemons.
+```
+
+When rendered, this produces:
+
+ - [X] Preheat oven to 450 degrees.
+ - [ ] Zest 35 lemons.
+
+Code Blocks
+===========
+
+Make **code blocks** by indenting two spaces:
+
+ f(x, y);
+
+You can also use three backticks to enclose the code block:
+
+ ```f(x, y);
+ g(f);```
+
+You can specify a language for syntax highlighting with `lang=xxx`:
+
+ lang=text
+ lang=html
+ <a href="#">...</a>
+
+When using fenced code blocks (triple backticks) you can simply append the
+language right after the backticks, like this: ##```html##
+
+This will highlight the block using a highlighter for that language, if one is
+available (in most cases, this means you need to configure Pygments):
+
+ lang=html
+ <a href="#">...</a>
+
+You can also use a `COUNTEREXAMPLE` header to show that a block of code is
+bad and shouldn't be copied:
+
+ lang=text
+ COUNTEREXAMPLE
+ function f() {
+ global \$\$variable_variable;
+ }
+
+This produces a block like this:
+
+ COUNTEREXAMPLE
+ function f() {
+ global \$\$variable_variable;
+ }
+
+You can use `lines=N` to limit the vertical size of a chunk of code, and
+`name=some_name.ext` to give it a name. For example, this:
+
+ lang=text
+ lang=html, name=example.html, lines=12, counterexample
+ ...
+
+...produces this:
+
+ lang=html, name=example.html, lines=12, counterexample
+ <p>Apple</p>
+ <p>Apricot</p>
+ <p>Avocado</p>
+ <p>Banana</p>
+ <p>Bilberry</p>
+ <p>Blackberry</p>
+ <p>Blackcurrant</p>
+ <p>Blueberry</p>
+ <p>Currant</p>
+ <p>Cherry</p>
+ <p>Cherimoya</p>
+ <p>Clementine</p>
+ <p>Date</p>
+ <p>Damson</p>
+ <p>Durian</p>
+ <p>Eggplant</p>
+ <p>Elderberry</p>
+ <p>Feijoa</p>
+ <p>Gooseberry</p>
+ <p>Grape</p>
+ <p>Grapefruit</p>
+ <p>Guava</p>
+ <p>Huckleberry</p>
+ <p>Jackfruit</p>
+ <p>Jambul</p>
+ <p>Kiwi fruit</p>
+ <p>Kumquat</p>
+ <p>Legume</p>
+ <p>Lemon</p>
+ <p>Lime</p>
+ <p>Lychee</p>
+ <p>Mandarine</p>
+ <p>Mango</p>
+ <p>Mangostine</p>
+ <p>Melon</p>
+
+
+You can use the `NOTE:`, `WARNING:` or `IMPORTANT:` elements to call attention
+to an important idea.
+
+For example, write this:
+
+```
+NOTE: Best practices in proton pack operation include not crossing the streams.
+```
+
+...to produce this:
+
+NOTE: Best practices in proton pack operation include not crossing the streams.
+
+Using `WARNING:` or `IMPORTANT:` at the beginning of the line changes the
+color of the callout:
+
+WARNING: Crossing the streams can result in total protonic reversal!
+
+IMPORTANT: Don't cross the streams!
+
+In addition, you can use `(NOTE)`, `(WARNING)`, or `(IMPORTANT)` to get the
+same effect but without `(NOTE)`, `(WARNING)`, or `(IMPORTANT)` appearing in
+the rendered result. For example, this callout uses `(NOTE)`:
+
+(NOTE) Dr. Egon Spengler is the best resource for additional proton pack
+ questions.
+
+
+Dividers
+========
+
+You can divide sections by putting three or more dashes on a line by
+themselves. This creates a divider or horizontal rule similar to an `<hr />`
+tag, like this one:
+
+---
+
+The dashes need to appear on their own line and be separated from other
+content. For example, like this:
+
+```
+This section will be visually separated.
+
+---
+
+On an entirely different topic, ...
+```
+
+
+= Linking URIs =
+
+URIs are automatically linked: http://phorge.it/
+
+If you have a URI with problematic characters in it, like
+"`http://comma.org/,`", you can surround it with angle brackets:
+
+ <http://comma.org/,>
+
+This will force the parser to consume the whole URI: <http://comma.org/,>
+
+You can also use create named links, where you choose the displayed text. These
+work within Phorge or on the internet at large:
+
+ [[/herald/transcript/ | Herald Transcripts]]
+ [[http://www.boring-legal-documents.com/ | exciting legal documents]]
+
+Markdown-style links are also supported:
+
+ [Toil](http://www.trouble.com)
+
+= Linking to Objects =
+
+You can link to Phorge objects, such as Differential revisions, Diffusion
+commits and Maniphest tasks, by mentioning the name of an object:
+
+ D123 # Link to Differential revision D123
+ rX123 # Link to SVN commit 123 from the "X" repository
+ rXaf3192cd5 # Link to Git commit "af3192cd5..." from the "X" repository.
+ # You must specify at least 7 characters of the hash.
+ T123 # Link to Maniphest task T123
+
+You can also link directly to a comment in Maniphest and Differential (these
+can be found on the date stamp of any transaction/comment):
+
+ T123#412 # Link to comment id #412 of task T123
+
+See the Phorge configuration setting `remarkup.ignored-object-names` to
+modify this behavior.
+
+= Embedding Objects
+
+You can also generate full-name references to some objects by using braces:
+
+ {D123} # Link to Differential revision D123 with the full name
+ {T123} # Link to Maniphest task T123 with the full name
+
+These references will also show when an object changes state (for instance, a
+task or revision is closed). Some types of objects support rich embedding.
+
+== Linking to Project Tags
+
+Projects can be linked to with the use of a hashtag `#`. This works by default
+using the name of the Project (lowercase, underscored). Additionally you
+can set multiple additional hashtags by editing the Project details.
+
+ #qa, #quality_assurance
+
+== Embedding Mocks (Pholio)
+
+You can embed a Pholio mock by using braces to refer to it:
+
+ {M123}
+
+By default the first four images from the mock set are displayed. This behavior
+can be overridden with the **image** option. With the **image** option you can
+provide one or more image IDs to display.
+
+You can set the image (or images) to display like this:
+
+ {M123, image=12345}
+ {M123, image=12345 & 6789}
+
+== Embedding Pastes
+
+You can embed a Paste using braces:
+
+ {P123}
+
+You can adjust the embed height with the `lines` option:
+
+ {P123, lines=15}
+
+You can highlight specific lines with the `highlight` option:
+
+ {P123, highlight=15}
+ {P123, highlight="23-25, 31"}
+
+== Embedding Images
+
+You can embed an image or other file by using braces to refer to it:
+
+ {F123}
+
+In most interfaces, you can drag-and-drop an image from your computer into the
+text area to upload and reference it.
+
+Some browsers (e.g. Chrome) support uploading an image data just by pasting them
+from clipboard into the text area.
+
+You can set file display options like this:
+
+ {F123, layout=left, float, size=full, alt="a duckling"}
+
+Valid options for all files are:
+
+ - **layout** left (default), center, right, inline, link (render a link
+ instead of a thumbnail for images)
+ - **name** with `layout=link` or for non-images, use this name for the link
+ text
+ - **alt** Provide alternate text for assistive technologies.
+
+Image files support these options:
+
+ - **float** If layout is set to left or right, the image will be floated so
+ text wraps around it.
+ - **size** thumb (default), full
+ - **width** Scale image to a specific width.
+ - **height** Scale image to a specific height.
+
+Audio and video files support these options:
+
+ - **media**: Specify the media type as `audio` or `video`. This allows you
+ to disambiguate how file format which may contain either audio or video
+ should be rendered.
+ - **loop**: Loop this media.
+ - **autoplay**: Automatically begin playing this media.
+
+== Embedding Countdowns
+
+You can embed a countdown by using braces:
+
+ {C123}
+
+= Quoting Text =
+
+To quote text, preface it with an `>`:
+
+ > This is quoted text.
+
+This appears like this:
+
+> This is quoted text.
+
+= Embedding Media =
+
+If you set a configuration flag, you can embed media directly in text:
+
+ - **remarkup.enable-embedded-youtube**: allows you to paste in YouTube videos
+ and have them render inline.
+
+This option is disabled by default because it has security and/or
+silliness implications. Carefully read the description before enabling it.
+
+= Image Macros =
+
+You can upload image macros (More Stuff -> Macro) which will replace text
+strings with the image you specify. For instance, you could upload an image of a
+dancing banana to create a macro named "peanutbutterjellytime", and then any
+time you type that string on a separate line it will be replaced with the image
+of a dancing banana.
+
+= Memes =
+
+You can also use image macros in the context of memes. For example, if you
+have an image macro named `grumpy`, you can create a meme by doing the
+following:
+
+ {meme, src = grumpy, above = toptextgoeshere, below = bottomtextgoeshere}
+
+By default, the font used to create the text for the meme is `tuffy.ttf`. For
+the more authentic feel of `impact.ttf`, you simply have to place the Impact
+TrueType font in the Phorge subfolder `/resources/font/`. If Remarkup
+detects the presence of `impact.ttf`, it will automatically use it.
+
+= Mentioning Users =
+
+In Differential and Maniphest, you can mention another user by writing:
+
+ @username
+
+When you submit your comment, this will add them as a CC on the revision or task
+if they aren't already CC'd.
+
+Icons
+=====
+
+You can add icons to comments using the `{icon ...}` syntax. For example:
+
+ {icon camera}
+
+This renders: {icon camera}
+
+You can select a color for icons:
+
+ {icon camera color=blue}
+
+This renders: {icon camera color=blue}
+
+For a list of available icons and colors, check the UIExamples application.
+(The icons are sourced from
+[[ https://fontawesome.com/v4.7.0/icons/ | FontAwesome ]], so you can also
+browse the collection there.)
+
+You can add `spin` to make the icon spin:
+
+ {icon cog spin}
+
+This renders: {icon cog spin}
+
+
+= Phriction Documents =
+
+You can link to Phriction documents with a name or path:
+
+ Make sure you sign and date your [[legal/Letter of Marque and Reprisal]]!
+
+By default, the link will render with the document title as the link name.
+With a pipe (`|`), you can retitle the link. Use this to mislead your
+opponents:
+
+ Check out these [[legal/boring_documents/ | exciting legal documents]]!
+
+Links to pages which do not exist are shown in red. Links to pages which exist
+but which the viewer does not have permission to see are shown with a lock
+icon, and the link will not disclose the page title.
+
+If you begin a link path with `./` or `../`, the remainder of the path will be
+evaluated relative to the current wiki page. For example, if you are writing
+content for the document `fruit/` a link to `[[./guava]]` is the same as a link
+to `[[fruit/guava]]` from elsewhere.
+
+Relative links may use `../` to transverse up the document tree. From the
+`produce/vegetables/` page, you can use `[[../fruit/guava]]` to link to the
+`produce/fruit/guava` page.
+
+Relative links do not work when used outside of wiki pages. For example,
+you can't use a relative link in a comment on a task, because there is no
+reasonable place for the link to start resolving from.
+
+When documents are moved, relative links are not automatically updated: they
+are preserved as currently written. After moving a document, you may need to
+review and adjust any relative links it contains.
+
+
+= Literal Blocks =
+
+To place text in a literal block use `%%%`:
+
+ %%%Text that won't be processed by remarkup
+ [[http://www.example.com | example]]
+ %%%
+
+Remarkup will not process the text inside of literal blocks (other than to
+escape HTML and preserve line breaks).
+
+= Tables =
+
+Remarkup supports simple table syntax. For example, this:
+
+```
+| Fruit | Color | Price | Peel?
+| ----- | ----- | ----- | -----
+| Apple | red | `\$0.93` | no
+| Banana | yellow | `\$0.19` | **YES**
+```
+
+...produces this:
+
+| Fruit | Color | Price | Peel?
+| ----- | ----- | ----- | -----
+| Apple | red | `\$0.93` | no
+| Banana | yellow | `\$0.19` | **YES**
+
+Remarkup also supports a simplified HTML table syntax. For example, this:
+
+```
+<table>
+ <tr>
+ <th>Fruit</th>
+ <th>Color</th>
+ <th>Price</th>
+ <th>Peel?</th>
+ </tr>
+ <tr>
+ <td>Apple</td>
+ <td>red</td>
+ <td>`\$0.93`</td>
+ <td>no</td>
+ </tr>
+ <tr>
+ <td>Banana</td>
+ <td>yellow</td>
+ <td>`\$0.19`</td>
+ <td>**YES**</td>
+ </tr>
+</table>
+```
+
+...produces this:
+
+<table>
+ <tr>
+ <th>Fruit</th>
+ <th>Color</th>
+ <th>Price</th>
+ <th>Peel?</th>
+ </tr>
+ <tr>
+ <td>Apple</td>
+ <td>red</td>
+ <td>`\$0.93`</td>
+ <td>no</td>
+ </tr>
+ <tr>
+ <td>Banana</td>
+ <td>yellow</td>
+ <td>`\$0.19`</td>
+ <td>**YES**</td>
+ </tr>
+</table>
+
+Some general notes about this syntax:
+
+ - your tags must all be properly balanced;
+ - your tags must NOT include attributes (`<td>` is OK, `<td style="...">` is
+ not);
+ - you can use other Remarkup rules (like **bold**, //italics//, etc.) inside
+ table cells.
+
+Navigation Sequences
+====================
+
+You can use `{nav ...}` to render a stylized navigation sequence when helping
+someone to locate something. This can be useful when writing documentation.
+For example, you could give someone directions to purchase lemons:
+
+{nav icon=home, name=Home >
+Grocery Store >
+Produce Section >
+icon=lemon-o, name=Lemons}
+
+To render this example, use this markup:
+
+```
+{nav icon=home, name=Home >
+Grocery Store >
+Produce Section >
+icon=lemon-o, name=Lemons}
+```
+
+In general:
+
+ - Separate sections with `>`.
+ - Each section can just have a name to add an element to the navigation
+ sequence, or a list of key-value pairs.
+ - Supported keys are `icon`, `name`, `type` and `href`.
+ - The `type` option can be set to `instructions` to indicate that an element
+ is asking the user to make a choice or follow specific instructions.
+
+Keystrokes
+==========
+
+You can use `{key ...}` to render a stylized keystroke. For example, this:
+
+```
+Press {key M} to view the starmap.
+```
+
+...renders this:
+
+> Press {key M} to view the starmap.
+
+You can also render sequences with modifier keys. This:
+
+```
+Use {key command option shift 3} to take a screenshot.
+Press {key down down-right right LP} to activate the hadoken technique.
+```
+
+...renders this:
+
+> Use {key command option shift 3} to take a screenshot.
+> Press {key down down-right right LP} to activate the hadoken technique.
+
+
+Anchors
+========
+
+You can use `{anchor #xyz}` to create a document anchor and later link to
+it directly with `#xyz` in the URI.
+
+Headers also automatically create named anchors.
+
+If you navigate to `#xyz` in your browser location bar, the page will scroll
+to the first anchor with "xyz" as a prefix of the anchor name.
+
+
+= Fullscreen Mode =
+
+Remarkup editors provide a fullscreen composition mode. This can make it easier
+to edit large blocks of text, or improve focus by removing distractions. You can
+exit **Fullscreen** mode by clicking the button again or by pressing escape.
+EOTEXT;
+
+ $remarkup_syntax_documentation_providers = id(new PhutilClassMapQuery())
+ ->setAncestorClass('RemarkupSyntaxDocumentationProvider')
+ ->execute();
+
+ // add custom Remarkup help
+ $content .= "\r\n\r\n";
+ foreach ($remarkup_syntax_documentation_providers as $doc_provider) {
+ $content .= $doc_provider->getDocumentation()."\r\n\r\n";
+ }
+
+ return $content;
+ }
+}
diff --git a/src/infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php b/src/infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php
@@ -0,0 +1,5 @@
+<?php
+
+interface RemarkupSyntaxDocumentationProvider {
+ public function getDocumentation();
+}
diff --git a/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php b/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php
--- a/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php
+++ b/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php
@@ -1,7 +1,8 @@
<?php
final class PhabricatorRemarkupCowsayBlockInterpreter
- extends PhutilRemarkupBlockInterpreter {
+ extends PhutilRemarkupBlockInterpreter
+ implements RemarkupSyntaxDocumentationProvider {
public function getInterpreterName() {
return 'cowsay';
@@ -71,4 +72,22 @@
return $map;
}
+ public function getDocumentation() {
+ return <<<EOT
+= Cowsay
+
+Cowsay is an application by Tony Monroe which has been ported over to
+Phabricator/Phorge to allow your comments to be voiced by
+a cow.
+
+A basic example of using cowsay would be to add a comment
+ cowsay{{{Why don't they play poker in the jungle? Too many cheetahs}}}
+which generates:
+
+cowsay{{{Why don't they play poker in the jungle? Too many cheetahs}}}
+
+More information about Cowsay can be found [[/reference/cowsay/ | here]]
+EOT;
+ }
+
}
diff --git a/src/infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php b/src/infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php
--- a/src/infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php
+++ b/src/infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php
@@ -1,7 +1,8 @@
<?php
final class PhabricatorRemarkupFigletBlockInterpreter
- extends PhutilRemarkupBlockInterpreter {
+ extends PhutilRemarkupBlockInterpreter
+ implements RemarkupSyntaxDocumentationProvider {
public function getInterpreterName() {
return 'figlet';
@@ -68,4 +69,19 @@
return $map;
}
+ public function getDocumentation() {
+ return <<<EOT
+= Figlet
+The `figlet` interpreter allows you to write some large text.
+For example, this:
+
+```figlet{{{Some big text!}}}```
+
+...produces this:
+
+figlet{{{Some big text!}}}
+
+More information about Figlet can be found [[/reference/figlet/ | here]]
+EOT;
+ }
}
diff --git a/src/view/form/control/PhabricatorRemarkupControl.php b/src/view/form/control/PhabricatorRemarkupControl.php
--- a/src/view/form/control/PhabricatorRemarkupControl.php
+++ b/src/view/form/control/PhabricatorRemarkupControl.php
@@ -255,7 +255,7 @@
$actions['fa-book'] = array(
'tip' => pht('Help'),
'align' => 'right',
- 'href' => PhabricatorEnv::getDoclink('Remarkup Reference'),
+ 'href' => '/reference/remarkup/',
);
$mode_actions = array();

File Metadata

Mime Type
text/plain
Expires
Fri, Dec 20, 10:43 (17 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1017287
Default Alt Text
D25698.1734691381.diff (46 KB)

Event Timeline