Commit f986b4c4 authored by Horacio Sanson's avatar Horacio Sanson Committed by Sean McGivern

Add support for PlantUML diagrams in Asciidoc.

This MR enables rendering of PlantUML diagrams in Asciidoc documents. To add a
PlantUML diagram all we need is to include a plantuml block like:

```
[plantuml, id="myDiagram", width="100px", height="100px"]
--
bob -> alice : ping
alice -> bob : pong
--
```

The plantuml block is substituted by an HTML img element with *src* pointing to
an external PlantUML server.

This MR also add a PlantUML integration section to the Administrator -> Settings
page to configure the PlantUML rendering service and to enable/disable it.

Closes: #17603
parent 826adaaf
...@@ -101,7 +101,7 @@ gem 'seed-fu', '~> 2.3.5' ...@@ -101,7 +101,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie' gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie'
gem 'gitlab-markup', '~> 1.5.1' gem 'gitlab-markup', '~> 1.5.0'
gem 'redcarpet', '~> 3.3.3' gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 4.2' gem 'rdoc', '~> 4.2'
...@@ -109,6 +109,7 @@ gem 'org-ruby', '~> 0.9.12' ...@@ -109,6 +109,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'asciidoctor-plantuml', '0.0.6'
gem 'rouge', '~> 2.0' gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.8' gem 'truncato', '~> 0.7.8'
......
...@@ -54,6 +54,8 @@ GEM ...@@ -54,6 +54,8 @@ GEM
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
asciidoctor-plantuml (0.0.6)
asciidoctor (~> 1.5)
ast (2.3.0) ast (2.3.0)
attr_encrypted (3.0.3) attr_encrypted (3.0.3)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
...@@ -841,6 +843,7 @@ DEPENDENCIES ...@@ -841,6 +843,7 @@ DEPENDENCIES
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.4.0) asana (~> 0.4.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
asciidoctor-plantuml (= 0.0.6)
attr_encrypted (~> 3.0.0) attr_encrypted (~> 3.0.0)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)
......
...@@ -101,6 +101,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -101,6 +101,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:html_emails_enabled, :html_emails_enabled,
:koding_enabled, :koding_enabled,
:koding_url, :koding_url,
:plantuml_enabled,
:plantuml_url,
:max_artifacts_size, :max_artifacts_size,
:max_attachment_size, :max_attachment_size,
:metrics_enabled, :metrics_enabled,
......
...@@ -68,6 +68,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -68,6 +68,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true, presence: true,
if: :koding_enabled if: :koding_enabled
validates :plantuml_url,
presence: true,
if: :plantuml_enabled
validates :max_attachment_size, validates :max_attachment_size,
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
...@@ -184,6 +188,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -184,6 +188,8 @@ class ApplicationSetting < ActiveRecord::Base
akismet_enabled: false, akismet_enabled: false,
koding_enabled: false, koding_enabled: false,
koding_url: nil, koding_url: nil,
plantuml_enabled: false,
plantuml_url: nil,
repository_checks_enabled: true, repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [], disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false, send_user_confirmation_email: false,
......
...@@ -420,6 +420,23 @@ ...@@ -420,6 +420,23 @@
= succeed "." do = succeed "." do
= link_to "Koding administration documentation", help_page_path("administration/integration/koding") = link_to "Koding administration documentation", help_page_path("administration/integration/koding")
%fieldset
%legend PlantUML
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :plantuml_enabled do
= f.check_box :plantuml_enabled
Enable PlantUML
.form-group
= f.label :plantuml_url, 'PlantUML URL', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://gitlab.your-plantuml-instance.com:8080'
.help-block
Allow rendering of
= link_to "PlantUML", "http://plantuml.com"
diagrams in Asciidoc documents using an external PlantUML service.
%fieldset %fieldset
%legend Usage statistics %legend Usage statistics
.form-group .form-group
......
---
title: Add support for PlantUML diagrams in AsciiDoc documents.
merge_request: 7810
author: Horacio Sanson
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddPlantUmlUrlToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :application_settings, :plantuml_url, :string
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddPlantUmlEnabledToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :application_settings, :plantuml_enabled, :boolean
end
end
...@@ -107,6 +107,8 @@ ActiveRecord::Schema.define(version: 20170106172224) do ...@@ -107,6 +107,8 @@ ActiveRecord::Schema.define(version: 20170106172224) do
t.integer "housekeeping_full_repack_period", default: 50, null: false t.integer "housekeeping_full_repack_period", default: 50, null: false
t.integer "housekeeping_gc_period", default: 200, null: false t.integer "housekeeping_gc_period", default: 200, null: false
t.boolean "html_emails_enabled", default: true t.boolean "html_emails_enabled", default: true
t.string "plantuml_url"
t.boolean "plantuml_enabled"
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
......
# PlantUML & GitLab
> [Introduced][ce-7810] in GitLab 8.16.
When [PlantUML](http://plantuml.com) integration is enabled and configured in
GitLab we are able to create simple diagrams in AsciiDoc documents created in
snippets, wikis, and repos.
## PlantUML Server
Before you can enable PlantUML in GitLab; you need to set up your own PlantUML
server that will generate the diagrams. Installing and configuring your
own PlantUML server is easy in Debian/Ubuntu distributions using Tomcat.
First you need to create a `plantuml.war` file from the source code:
```
sudo apt-get install graphviz openjdk-7-jdk git-core maven
git clone https://github.com/plantuml/plantuml-server.git
cd plantuml-server
mvn package
```
The above sequence of commands will generate a WAR file that can be deployed
using Tomcat:
```
sudo apt-get install tomcat7
sudo cp target/plantuml.war /var/lib/tomcat7/webapps/plantuml.war
sudo chown tomcat7:tomcat7 /var/lib/tomcat7/webapps/plantuml.war
sudo service restart tomcat7
```
Once the Tomcat service restarts the PlantUML service will be ready and
listening for requests on port 8080:
```
http://localhost:8080/plantuml
```
you can change these defaults by editing the `/etc/tomcat7/server.xml` file.
## GitLab
You need to enable PlantUML integration from Settings under Admin Area. To do
that, login with an Admin account and do following:
- in GitLab go to **Admin Area** and then **Settings**
- scroll to bottom of the page until PlantUML section
- check **Enable PlantUML** checkbox
- set the PlantUML instance as **PlantUML URL**
## Creating Diagrams
With PlantUML integration enabled and configured, we can start adding diagrams to
our AsciiDoc snippets, wikis and repos using blocks:
```
[plantuml, format="png", id="myDiagram", width="200px"]
--
Bob->Alice : hello
Alice -> Bob : Go Away
--
```
The above block will be converted to an HTML img tag with source pointing to the
PlantUML instance. If the PlantUML server is correctly configured, this should
render a nice diagram instead of the block:
![PlantUML Integration](../img/integration/plantuml-example.png)
Inside the block you can add any of the supported diagrams by PlantUML such as
[Sequence](http://plantuml.com/sequence-diagram), [Use Case](http://plantuml.com/use-case-diagram),
[Class](http://plantuml.com/class-diagram), [Activity](http://plantuml.com/activity-diagram-legacy),
[Component](http://plantuml.com/component-diagram), [State](http://plantuml.com/state-diagram),
and [Object](http://plantuml.com/object-diagram) diagrams. You do not need to use the PlantUML
diagram delimiters `@startuml`/`@enduml` as these are replaced by the AsciiDoc `plantuml` block.
Some parameters can be added to the block definition:
- *format*: Can be either `png` or `svg`. Note that `svg` is not supported by
all browsers so use with care. The default is `png`.
- *id*: A CSS id added to the diagram HTML tag.
- *width*: Width attribute added to the img tag.
- *height*: Height attribute added to the img tag.
...@@ -44,7 +44,9 @@ Example response: ...@@ -44,7 +44,9 @@ Example response:
"repository_storage": "default", "repository_storage": "default",
"repository_storages": ["default"], "repository_storages": ["default"],
"koding_enabled": false, "koding_enabled": false,
"koding_url": null "koding_url": null,
"plantuml_enabled": false,
"plantuml_url": null
} }
``` ```
...@@ -80,6 +82,8 @@ PUT /application/settings ...@@ -80,6 +82,8 @@ PUT /application/settings
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. | | `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. | | `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
| `disabled_oauth_sign_in_sources` | Array of strings | no | Disabled OAuth sign-in sources | | `disabled_oauth_sign_in_sources` | Array of strings | no | Disabled OAuth sign-in sources |
| `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. |
| `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. |
```bash ```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1 curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
...@@ -112,6 +116,8 @@ Example response: ...@@ -112,6 +116,8 @@ Example response:
"container_registry_token_expire_delay": 5, "container_registry_token_expire_delay": 5,
"repository_storage": "default", "repository_storage": "default",
"koding_enabled": false, "koding_enabled": false,
"koding_url": null "koding_url": null,
"plantuml_enabled": false,
"plantuml_url": null
} }
``` ```
...@@ -16,6 +16,7 @@ See the documentation below for details on how to configure these services. ...@@ -16,6 +16,7 @@ See the documentation below for details on how to configure these services.
- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users - [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users
- [Akismet](akismet.md) Configure Akismet to stop spam - [Akismet](akismet.md) Configure Akismet to stop spam
- [Koding](../administration/integration/koding.md) Configure Koding to use IDE integration - [Koding](../administration/integration/koding.md) Configure Koding to use IDE integration
- [PlantUML](../administration/integration/plantuml.md) Configure PlantUML to use diagrams in AsciiDoc documents.
GitLab Enterprise Edition contains [advanced Jenkins support][jenkins]. GitLab Enterprise Edition contains [advanced Jenkins support][jenkins].
......
...@@ -565,6 +565,8 @@ module API ...@@ -565,6 +565,8 @@ module API
expose :repository_storages expose :repository_storages
expose :koding_enabled expose :koding_enabled
expose :koding_url expose :koding_url
expose :plantuml_enabled
expose :plantuml_url
end end
class Release < Grape::Entity class Release < Grape::Entity
......
...@@ -93,6 +93,10 @@ module API ...@@ -93,6 +93,10 @@ module API
given koding_enabled: ->(val) { val } do given koding_enabled: ->(val) { val } do
requires :koding_url, type: String, desc: 'The Koding team URL' requires :koding_url, type: String, desc: 'The Koding team URL'
end end
optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML'
given plantuml_enabled: ->(val) { val } do
requires :plantuml_url, type: String, desc: 'The PlantUML server URL'
end
optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.' optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.'
optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.' optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.' optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
...@@ -114,7 +118,7 @@ module API ...@@ -114,7 +118,7 @@ module API
:shared_runners_enabled, :max_artifacts_size, :container_registry_token_expire_delay, :shared_runners_enabled, :max_artifacts_size, :container_registry_token_expire_delay,
:metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled, :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
:akismet_enabled, :admin_notification_email, :sentry_enabled, :akismet_enabled, :admin_notification_email, :sentry_enabled,
:repository_storage, :repository_checks_enabled, :koding_enabled, :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
:version_check_enabled, :email_author_in_body, :html_emails_enabled, :version_check_enabled, :email_author_in_body, :html_emails_enabled,
:housekeeping_enabled :housekeeping_enabled
end end
......
require 'asciidoctor' require 'asciidoctor'
require 'asciidoctor/converter/html5' require 'asciidoctor/converter/html5'
require "asciidoctor-plantuml"
module Gitlab module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
...@@ -29,6 +30,8 @@ module Gitlab ...@@ -29,6 +30,8 @@ module Gitlab
) )
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS) asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
plantuml_setup
html = ::Asciidoctor.convert(input, asciidoc_opts) html = ::Asciidoctor.convert(input, asciidoc_opts)
html = Banzai.post_process(html, context) html = Banzai.post_process(html, context)
...@@ -36,6 +39,15 @@ module Gitlab ...@@ -36,6 +39,15 @@ module Gitlab
html.html_safe html.html_safe
end end
def self.plantuml_setup
Asciidoctor::PlantUml.configure do |conf|
conf.url = ApplicationSetting.current.plantuml_url
conf.svg_enable = ApplicationSetting.current.plantuml_enabled
conf.png_enable = ApplicationSetting.current.plantuml_enabled
conf.txt_enable = false
end
end
class Html5Converter < Asciidoctor::Converter::Html5Converter class Html5Converter < Asciidoctor::Converter::Html5Converter
extend Asciidoctor::Converter::Config extend Asciidoctor::Converter::Config
......
...@@ -35,6 +35,7 @@ module Gitlab ...@@ -35,6 +35,7 @@ module Gitlab
signin_enabled: Settings.gitlab['signin_enabled'], signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'], gravatar_enabled: Settings.gravatar['enabled'],
koding_enabled: false, koding_enabled: false,
plantuml_enabled: false,
sign_in_text: nil, sign_in_text: nil,
after_sign_up_text: nil, after_sign_up_text: nil,
help_page_text: nil, help_page_text: nil,
......
...@@ -8,6 +8,10 @@ module Gitlab ...@@ -8,6 +8,10 @@ module Gitlab
let(:html) { 'H<sub>2</sub>O' } let(:html) { 'H<sub>2</sub>O' }
context "without project" do context "without project" do
before do
allow_any_instance_of(ApplicationSetting).to receive(:current).and_return(::ApplicationSetting.create_from_defaults)
end
it "converts the input using Asciidoctor and default options" do it "converts the input using Asciidoctor and default options" do
expected_asciidoc_opts = { expected_asciidoc_opts = {
safe: :secure, safe: :secure,
......
...@@ -16,6 +16,8 @@ describe API::Settings, 'Settings', api: true do ...@@ -16,6 +16,8 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['repository_storage']).to eq('default') expect(json_response['repository_storage']).to eq('default')
expect(json_response['koding_enabled']).to be_falsey expect(json_response['koding_enabled']).to be_falsey
expect(json_response['koding_url']).to be_nil expect(json_response['koding_url']).to be_nil
expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil
end end
end end
...@@ -28,7 +30,8 @@ describe API::Settings, 'Settings', api: true do ...@@ -28,7 +30,8 @@ describe API::Settings, 'Settings', api: true do
it "updates application settings" do it "updates application settings" do
put api("/application/settings", admin), put api("/application/settings", admin),
default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com' default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com',
plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com'
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3) expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['signin_enabled']).to be_falsey expect(json_response['signin_enabled']).to be_falsey
...@@ -36,6 +39,8 @@ describe API::Settings, 'Settings', api: true do ...@@ -36,6 +39,8 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['repository_storages']).to eq(['custom']) expect(json_response['repository_storages']).to eq(['custom'])
expect(json_response['koding_enabled']).to be_truthy expect(json_response['koding_enabled']).to be_truthy
expect(json_response['koding_url']).to eq('http://koding.example.com') expect(json_response['koding_url']).to eq('http://koding.example.com')
expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
end end
end end
...@@ -47,5 +52,14 @@ describe API::Settings, 'Settings', api: true do ...@@ -47,5 +52,14 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['error']).to eq('koding_url is missing') expect(json_response['error']).to eq('koding_url is missing')
end end
end end
context "missing plantuml_url value when plantuml_enabled is true" do
it "returns a blank parameter error message" do
put api("/application/settings", admin), plantuml_enabled: true
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('plantuml_url is missing')
end
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment