From f986b4c4e529f4c2518f0ce37dc9dfcaa2f073a0 Mon Sep 17 00:00:00 2001 From: Horacio Sanson <horacio@allm.net> Date: Tue, 29 Nov 2016 02:41:29 +0900 Subject: [PATCH] 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 --- Gemfile | 25 ++--- Gemfile.lock | 3 + .../admin/application_settings_controller.rb | 2 + app/models/application_setting.rb | 6 ++ .../application_settings/_form.html.haml | 17 ++++ .../unreleased/asciidoctor-plantuml.yml | 4 + ...d_plant_uml_url_to_application_settings.rb | 12 +++ ...ant_uml_enabled_to_application_settings.rb | 12 +++ db/schema.rb | 2 + .../img/integration/plantuml-example.png | Bin 0 -> 33034 bytes doc/administration/integration/plantuml.md | 87 ++++++++++++++++++ doc/api/settings.md | 10 +- doc/integration/README.md | 1 + lib/api/entities.rb | 2 + lib/api/settings.rb | 6 +- lib/gitlab/asciidoc.rb | 12 +++ lib/gitlab/current_settings.rb | 1 + spec/lib/gitlab/asciidoc_spec.rb | 4 + spec/requests/api/settings_spec.rb | 16 +++- 19 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 changelogs/unreleased/asciidoctor-plantuml.yml create mode 100644 db/migrate/20161201001911_add_plant_uml_url_to_application_settings.rb create mode 100644 db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb create mode 100644 doc/administration/img/integration/plantuml-example.png create mode 100644 doc/administration/integration/plantuml.md diff --git a/Gemfile b/Gemfile index a8141abafc25..b43604bd2536 100644 --- a/Gemfile +++ b/Gemfile @@ -99,18 +99,19 @@ gem 'unf', '~> 0.1.4' gem 'seed-fu', '~> 2.3.5' # Markdown and HTML processing -gem 'html-pipeline', '~> 1.11.0' -gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie' -gem 'gitlab-markup', '~> 1.5.1' -gem 'redcarpet', '~> 3.3.3' -gem 'RedCloth', '~> 4.3.2' -gem 'rdoc', '~> 4.2' -gem 'org-ruby', '~> 0.9.12' -gem 'creole', '~> 0.5.0' -gem 'wikicloth', '0.8.1' -gem 'asciidoctor', '~> 1.5.2' -gem 'rouge', '~> 2.0' -gem 'truncato', '~> 0.7.8' +gem 'html-pipeline', '~> 1.11.0' +gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie' +gem 'gitlab-markup', '~> 1.5.0' +gem 'redcarpet', '~> 3.3.3' +gem 'RedCloth', '~> 4.3.2' +gem 'rdoc', '~> 4.2' +gem 'org-ruby', '~> 0.9.12' +gem 'creole', '~> 0.5.0' +gem 'wikicloth', '0.8.1' +gem 'asciidoctor', '~> 1.5.2' +gem 'asciidoctor-plantuml', '0.0.6' +gem 'rouge', '~> 2.0' +gem 'truncato', '~> 0.7.8' # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM diff --git a/Gemfile.lock b/Gemfile.lock index bdd591e008a9..e2d7f94e571a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,6 +54,8 @@ GEM faraday_middleware-multi_json (~> 0.0) oauth2 (~> 1.0) asciidoctor (1.5.3) + asciidoctor-plantuml (0.0.6) + asciidoctor (~> 1.5) ast (2.3.0) attr_encrypted (3.0.3) encryptor (~> 3.0.0) @@ -841,6 +843,7 @@ DEPENDENCIES allocations (~> 1.0) asana (~> 0.4.0) asciidoctor (~> 1.5.2) + asciidoctor-plantuml (= 0.0.6) attr_encrypted (~> 3.0.0) awesome_print (~> 1.2.0) babosa (~> 1.0.2) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index e34ba4244975..1b4987dd7382 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -101,6 +101,8 @@ def application_setting_params_ce :html_emails_enabled, :koding_enabled, :koding_url, + :plantuml_enabled, + :plantuml_url, :max_artifacts_size, :max_attachment_size, :metrics_enabled, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index bf463a3b6bbd..8fab77cda0aa 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -68,6 +68,10 @@ class ApplicationSetting < ActiveRecord::Base presence: true, if: :koding_enabled + validates :plantuml_url, + presence: true, + if: :plantuml_enabled + validates :max_attachment_size, presence: true, numericality: { only_integer: true, greater_than: 0 } @@ -184,6 +188,8 @@ def self.create_from_defaults akismet_enabled: false, koding_enabled: false, koding_url: nil, + plantuml_enabled: false, + plantuml_url: nil, repository_checks_enabled: true, disabled_oauth_sign_in_sources: [], send_user_confirmation_email: false, diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 4612a7a058ac..558bbe07b160 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -420,6 +420,23 @@ = succeed "." do = 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 %legend Usage statistics .form-group diff --git a/changelogs/unreleased/asciidoctor-plantuml.yml b/changelogs/unreleased/asciidoctor-plantuml.yml new file mode 100644 index 000000000000..ba6ef7c08002 --- /dev/null +++ b/changelogs/unreleased/asciidoctor-plantuml.yml @@ -0,0 +1,4 @@ +--- +title: Add support for PlantUML diagrams in AsciiDoc documents. +merge_request: 7810 +author: Horacio Sanson diff --git a/db/migrate/20161201001911_add_plant_uml_url_to_application_settings.rb b/db/migrate/20161201001911_add_plant_uml_url_to_application_settings.rb new file mode 100644 index 000000000000..b8d8742ae402 --- /dev/null +++ b/db/migrate/20161201001911_add_plant_uml_url_to_application_settings.rb @@ -0,0 +1,12 @@ +# 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 diff --git a/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb b/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb new file mode 100644 index 000000000000..3677f978cc2c --- /dev/null +++ b/db/migrate/20161206003819_add_plant_uml_enabled_to_application_settings.rb @@ -0,0 +1,12 @@ +# 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 diff --git a/db/schema.rb b/db/schema.rb index f3bf7ced3930..c58a886b0fa4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -107,6 +107,8 @@ t.integer "housekeeping_full_repack_period", default: 50, null: false t.integer "housekeeping_gc_period", default: 200, null: false t.boolean "html_emails_enabled", default: true + t.string "plantuml_url" + t.boolean "plantuml_enabled" end create_table "audit_events", force: :cascade do |t| diff --git a/doc/administration/img/integration/plantuml-example.png b/doc/administration/img/integration/plantuml-example.png new file mode 100644 index 0000000000000000000000000000000000000000..cb64eca1a8ae7111d3cfe18165ec91015f6566d7 GIT binary patch literal 33034 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 Gul^6xz`7j( literal 0 HcmV?d00001 diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md new file mode 100644 index 000000000000..e5cf592e0a66 --- /dev/null +++ b/doc/administration/integration/plantuml.md @@ -0,0 +1,87 @@ +# 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: + + + +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. + diff --git a/doc/api/settings.md b/doc/api/settings.md index 0bd38a6e664a..f86c7cc2f941 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -44,7 +44,9 @@ Example response: "repository_storage": "default", "repository_storages": ["default"], "koding_enabled": false, - "koding_url": null + "koding_url": null, + "plantuml_enabled": false, + "plantuml_url": null } ``` @@ -80,6 +82,8 @@ PUT /application/settings | `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. | | `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 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: "container_registry_token_expire_delay": 5, "repository_storage": "default", "koding_enabled": false, - "koding_url": null + "koding_url": null, + "plantuml_enabled": false, + "plantuml_url": null } ``` diff --git a/doc/integration/README.md b/doc/integration/README.md index ed843c0bfa98..e97430feb573 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -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 - [Akismet](akismet.md) Configure Akismet to stop spam - [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]. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d2fadf6a3d07..885ce7d44bc6 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -565,6 +565,8 @@ class ApplicationSetting < Grape::Entity expose :repository_storages expose :koding_enabled expose :koding_url + expose :plantuml_enabled + expose :plantuml_url end class Release < Grape::Entity diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 9eb9a105bde6..c5eff16a5de2 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -93,6 +93,10 @@ def current_settings given koding_enabled: ->(val) { val } do requires :koding_url, type: String, desc: 'The Koding team URL' 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 :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.' @@ -114,7 +118,7 @@ def current_settings :shared_runners_enabled, :max_artifacts_size, :container_registry_token_expire_delay, :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_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, :housekeeping_enabled end diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index fa2342843612..0618107e2c3f 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -1,5 +1,6 @@ require 'asciidoctor' require 'asciidoctor/converter/html5' +require "asciidoctor-plantuml" module Gitlab # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters @@ -29,6 +30,8 @@ def self.render(input, context, asciidoc_opts = {}) ) asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS) + plantuml_setup + html = ::Asciidoctor.convert(input, asciidoc_opts) html = Banzai.post_process(html, context) @@ -36,6 +39,15 @@ def self.render(input, context, asciidoc_opts = {}) html.html_safe 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 extend Asciidoctor::Converter::Config diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 9d142f1b82e8..2ff27e46d646 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -35,6 +35,7 @@ def fake_application_settings signin_enabled: Settings.gitlab['signin_enabled'], gravatar_enabled: Settings.gravatar['enabled'], koding_enabled: false, + plantuml_enabled: false, sign_in_text: nil, after_sign_up_text: nil, help_page_text: nil, diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index f3843ca64ff6..ba199917f5c8 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -8,6 +8,10 @@ module Gitlab let(:html) { 'H<sub>2</sub>O' } 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 expected_asciidoc_opts = { safe: :secure, diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index ad9d8a25af45..91e3c333a02e 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -16,6 +16,8 @@ expect(json_response['repository_storage']).to eq('default') expect(json_response['koding_enabled']).to be_falsey 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 @@ -28,7 +30,8 @@ it "updates application settings" do 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(json_response['default_projects_limit']).to eq(3) expect(json_response['signin_enabled']).to be_falsey @@ -36,6 +39,8 @@ expect(json_response['repository_storages']).to eq(['custom']) expect(json_response['koding_enabled']).to be_truthy 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 @@ -47,5 +52,14 @@ expect(json_response['error']).to eq('koding_url is missing') 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 -- GitLab