diff --git a/Gemfile b/Gemfile index a8141abafc255ff4e9fcff9ab94610358b409a86..b43604bd25367ede5187a0f27707b13faf98ab76 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 bdd591e008a93c4285fdfd63a6f3984ecb7b43a2..e2d7f94e571a62fb93a1ffaaa6aee5c5858a0e9f 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 e34ba4244975495487f3685255e584f25c177969..1b4987dd7382a4de6fe3016ab5025f5b9c566424 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 bf463a3b6bbd6d26e54a756f46ba7f58091dbd65..8fab77cda0aa8293f846d320a7a330156bd6ff03 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 4612a7a058ac90a56df48e2068d22356bc2d9a7b..558bbe07b1603f52478f9f9ab444a1852e156d3b 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 0000000000000000000000000000000000000000..ba6ef7c0800268e9ce721667b20c6f1b344d532d --- /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 0000000000000000000000000000000000000000..b8d8742ae402bd50539475de46beb1871db69bdd --- /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 0000000000000000000000000000000000000000..3677f978cc2cfada199b9cf6142593c42c6dc5c6 --- /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 f3bf7ced3930ef224229f4b9ee80e0cc199b432d..c58a886b0fa4ab019daab74353e77a2053d07fcd 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 Binary files /dev/null and b/doc/administration/img/integration/plantuml-example.png differ diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md new file mode 100644 index 0000000000000000000000000000000000000000..e5cf592e0a660757847379006028235dbec58223 --- /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 0bd38a6e664ad976757bd3292e51f44cb9a350ea..f86c7cc2f941f016f385efd22f1cdafe4a7ebb59 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 ed843c0bfa9880596340b8cc4cb3828112225e26..e97430feb573a7b4ee2cc58511f3cd504c5973a8 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 d2fadf6a3d076ac87c38bb5e3d6434718b405754..885ce7d44bc667905815e35f13da6d7842c767ab 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 9eb9a105bde683c8e07a17012eeb4d0543f61661..c5eff16a5de222f8e0c1de18c195ec967b1dc9ad 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 fa23428436129d3c734d3d1329077ab60ecd7f96..0618107e2c3f6148ba4f3af455f1f2a00c28fb48 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 9d142f1b82e8503a7b9367dee11b043808f34392..2ff27e46d646766a129e0dea025ecb4a18b3c7e2 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 f3843ca64ff6fb5c9008b7ace6efeb9e3d142055..ba199917f5c82d8addf518810577f106ee280ffb 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 ad9d8a25af45e8b958183ba62ecf3e9a578a0f99..91e3c333a02e63fa8402e582cb7d2c7d68086910 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