diff --git a/domain/section/mermaid/mermaid.go b/domain/section/mermaid/mermaid.go deleted file mode 100644 index 48c8d9cc..00000000 --- a/domain/section/mermaid/mermaid.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 Documize Inc. . All rights reserved. -// -// This software (Documize Community Edition) is licensed under -// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html -// -// You can operate outside the AGPL restrictions by purchasing -// Documize Enterprise Edition and obtaining a commercial license -// by contacting . -// -// https://documize.com - -package mermaid - -import ( - "net/http" - - "github.com/documize/community/core/env" - "github.com/documize/community/domain" - "github.com/documize/community/domain/section/provider" -) - -// Provider represents Mermaid Diagram -type Provider struct { - Runtime *env.Runtime - Store *domain.Store -} - -// Meta describes us -func (*Provider) Meta() provider.TypeMeta { - section := provider.TypeMeta{} - - section.ID = "f1067a60-45e5-40b5-89f6-aa3b03dd7f35" - section.Title = "Mermaid Diagram" - section.Description = "Diagrams generated from textual descriptions" - section.ContentType = "mermaid" - section.PageType = "tab" - section.Order = 9990 - - return section -} - -// Command stub. -func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) { - provider.WriteEmpty(w) -} - -// Render returns data as-is (HTML). -func (*Provider) Render(ctx *provider.Context, config, data string) string { - return data -} - -// Refresh just sends back data as-is. -func (*Provider) Refresh(ctx *provider.Context, config, data string) string { - return data -} diff --git a/domain/section/plantuml/plantuml.go b/domain/section/plantuml/plantuml.go new file mode 100644 index 00000000..4ed471dc --- /dev/null +++ b/domain/section/plantuml/plantuml.go @@ -0,0 +1,116 @@ +// Copyright 2016 Documize Inc. . All rights reserved. +// +// This software (Documize Community Edition) is licensed under +// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html +// +// You can operate outside the AGPL restrictions by purchasing +// Documize Enterprise Edition and obtaining a commercial license +// by contacting . +// +// https://documize.com + +package plantuml + +import ( + "bytes" + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/documize/community/core/env" + "github.com/documize/community/domain" + "github.com/documize/community/domain/section/provider" +) + +// Provider represents Mermaid Diagram +type Provider struct { + Runtime *env.Runtime + Store *domain.Store +} + +// Meta describes us +func (*Provider) Meta() provider.TypeMeta { + section := provider.TypeMeta{} + + section.ID = "f1067a60-45e5-40b5-89f6-aa3b03dd7f35" + section.Title = "PlantUML Diagram" + section.Description = "Diagrams generated from text" + section.ContentType = "plantuml" + section.PageType = "tab" + section.Order = 9990 + + return section +} + +// Command stub. +func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + method := query.Get("method") + + if len(method) == 0 { + provider.WriteMessage(w, "plantuml", "missing method name") + return + } + + switch method { + case "preview": + var payload struct { + Data string `json:"data"` + } + + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + if err != nil { + provider.WriteMessage(w, "plantuml", "Bad payload") + return + } + + err = json.Unmarshal(body, &payload) + if err != nil { + provider.WriteMessage(w, "plantuml", "Cannot unmarshal") + return + } + + diagram := p.generateDiagram(ctx, payload.Data) + payload.Data = diagram + provider.WriteJSON(w, payload) + return + } + + provider.WriteEmpty(w) +} + +// Render returns data as-is (HTML). +func (p *Provider) Render(ctx *provider.Context, config, data string) string { + return p.generateDiagram(ctx, data) +} + +// Refresh just sends back data as-is. +func (*Provider) Refresh(ctx *provider.Context, config, data string) string { + return data +} + +func (p *Provider) generateDiagram(ctx *provider.Context, data string) string { + org, _ := p.Store.Organization.GetOrganization(ctx.Request, ctx.OrgID) + + var transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, // TODO should be glick.InsecureSkipVerifyTLS (from -insecure flag) but get error: x509: certificate signed by unknown authority + }} + client := &http.Client{Transport: transport} + + resp, _ := client.Post(org.ConversionEndpoint+"/api/plantuml", "application/text", bytes.NewReader([]byte(data))) + defer func() { + if e := resp.Body.Close(); e != nil { + fmt.Println("resp.Body.Close error: " + e.Error()) + } + }() + + png, _ := ioutil.ReadAll(resp.Body) + pngEncoded := base64.StdEncoding.EncodeToString(png) + + return string(fmt.Sprintf("data:image/png;base64, %s", pngEncoded)) +} diff --git a/domain/section/register.go b/domain/section/register.go index bf60f3f9..c86fa5c6 100644 --- a/domain/section/register.go +++ b/domain/section/register.go @@ -21,8 +21,8 @@ import ( "github.com/documize/community/domain/section/gemini" "github.com/documize/community/domain/section/github" "github.com/documize/community/domain/section/markdown" - // "github.com/documize/community/domain/section/mermaid" "github.com/documize/community/domain/section/papertrail" + "github.com/documize/community/domain/section/plantuml" "github.com/documize/community/domain/section/provider" "github.com/documize/community/domain/section/table" "github.com/documize/community/domain/section/trello" @@ -41,7 +41,7 @@ func Register(rt *env.Runtime, s *domain.Store) { provider.Register("trello", &trello.Provider{Runtime: rt, Store: s}) provider.Register("wysiwyg", &wysiwyg.Provider{Runtime: rt, Store: s}) provider.Register("airtable", &airtable.Provider{Runtime: rt, Store: s}) - // provider.Register("mermaid", &mermaid.Provider{Runtime: rt, Store: s}) + provider.Register("plantuml", &plantuml.Provider{Runtime: rt, Store: s}) p := provider.List() rt.Log.Info(fmt.Sprintf("Registered %d sections", len(p))) diff --git a/gui/.eslintrc.js b/gui/.eslintrc.js index b6a983ed..ba635f23 100644 --- a/gui/.eslintrc.js +++ b/gui/.eslintrc.js @@ -44,22 +44,23 @@ module.exports = { } ], globals: { - "is": true, - "mermaid": true, - "_": true, - "tinymce": true, - "CodeMirror": true, - "Mousetrap": true, - "Sortable": true, - "moment": true, - "Dropzone": true, - "server": true, - "authenticateUser": true, - "stubAudit": true, - "stubUserNotification": true, - "userLogin": true, - "Keycloak": true, - "slug": true, - "interact": true - } + "is": true, + "mermaid": true, + "_": true, + "tinymce": true, + "CodeMirror": true, + "Mousetrap": true, + "Sortable": true, + "moment": true, + "Dropzone": true, + "server": true, + "authenticateUser": true, + "stubAudit": true, + "stubUserNotification": true, + "userLogin": true, + "Keycloak": true, + "slug": true, + "interact": true, + "velocity": true + } }; diff --git a/gui/app/components/section/mermaid/type-editor.js b/gui/app/components/section/mermaid/type-editor.js deleted file mode 100644 index d99a3698..00000000 --- a/gui/app/components/section/mermaid/type-editor.js +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2016 Documize Inc. . All rights reserved. -// -// This software (Documize Community Edition) is licensed under -// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html -// -// You can operate outside the AGPL restrictions by purchasing -// Documize Enterprise Edition and obtaining a commercial license -// by contacting . -// -// https://documize.com - -import { computed, observer } from '@ember/object'; -import Component from '@ember/component'; - -export default Component.extend({ - isDirty: false, - diagramText: '', - diagramPreview: null, - config: null, - - editorId: computed('page', function () { - let page = this.get('page'); - return `mermaid-editor-${page.id}`; - }), - previewId: computed('page', function () { - let page = this.get('page'); - return `mermaid-preview-${page.id}`; - }), - // generateDiagram: observer('diagramText', function() { - // let txt = this.get('diagramText'); - // console.log('calc diaggram'); - - // let self = this; - // var cb = function(svg) { - // return svg; - // // self.set('diagramPreview', svg); - // }; - - // if (is.empty(this.get('diagramText'))) return ''; - - // mermaid.render(this.get('previewId'), txt, cb); - // }), - - keyUp() { - this.generateDiagram(); - }, - - generateDiagram() { - console.log('calc diaggram'); - - let txt = this.get('diagramText'); - if (is.empty(this.get('diagramText'))) this.set('diagramPreview', ''); - - let self = this; - var cb = function(svg) { - self.set('diagramPreview', svg); - }; - - mermaid.render(this.get('previewId'), txt, cb); - }, - - didReceiveAttrs() { - this._super(...arguments); - let config = {}; - mermaid.initialize({}); - console.log('dra'); - - try { - config = JSON.parse(this.get('meta.config')); - } catch (e) {} // eslint-disable-line no-empty - - if (is.empty(config)) { - config = { - txt: "" - }; - } - - this.set('diagramText', config.txt); - this.set('config', config); - - this.generateDiagram(); - }, - - // onType: function() { - // debounce(this, this.generateDiagram, 350); - // }.observes('diagramText'), - - actions: { - isDirty() { - return this.get('isDirty') || (this.get('diagramText') !== this.get('config.txt')); - }, - - onCancel() { - let cb = this.get('onCancel'); - cb(); - }, - - onAction(title) { - let page = this.get('page'); - let meta = this.get('meta'); - - meta.set('config', JSON.stringify({ txt: this.get('diagramText') })); - meta.set('rawBody', this.get('diagramPreview')); - page.set('body', this.get('diagramPreview')); - page.set('title', title); - - let cb = this.get('onAction'); - cb(page, meta); - }, - - onInsertFlowchart() { - let txt = `graph TB - c1-->a2 - subgraph one - a1-->a2 - end - subgraph two - b1-->b2 - end - subgraph three - c1-->c2 - end`; - - // this.set('diagramPreview', null); - this.set('diagramText', txt); - this.generateDiagram(); - }, - - onInsertSequence() { - let txt = `sequenceDiagram - participant Alice - participant Bob - Alice->John: Hello John, how are you? - loop Healthcheck - John->John: Fight against hypochondria - end - Note right of John: Rational thoughts
prevail... - John-->Alice: Great! - John->Bob: How about you? - Bob-->John: Jolly good!`; - - // this.set('diagramPreview', null); - this.set('diagramText', txt); - this.generateDiagram(); - }, - - onInsertGantt() { - let txt = `gantt - dateFormat YYYY-MM-DD - title Adding GANTT diagram functionality to mermaid - section A section - Completed task :done, des1, 2014-01-06,2014-01-08 - Active task :active, des2, 2014-01-09, 3d - Future task : des3, after des2, 5d - Future task2 : des4, after des3, 5d - section Critical tasks - Completed task in the critical line :crit, done, 2014-01-06,24h - Implement parser and jison :crit, done, after des1, 2d - Create tests for parser :crit, active, 3d - Future task in critical line :crit, 5d - Create tests for renderer :2d - Add to mermaid :1d`; - - // this.set('diagramPreview', null); - this.set('diagramText', txt); - this.generateDiagram(); - } - } -}); diff --git a/gui/app/components/section/plantuml/type-editor.js b/gui/app/components/section/plantuml/type-editor.js new file mode 100644 index 00000000..e74882ff --- /dev/null +++ b/gui/app/components/section/plantuml/type-editor.js @@ -0,0 +1,342 @@ +// Copyright 2016 Documize Inc. . All rights reserved. +// +// This software (Documize Community Edition) is licensed under +// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html +// +// You can operate outside the AGPL restrictions by purchasing +// Documize Enterprise Edition and obtaining a commercial license +// by contacting . +// +// https://documize.com + +import { schedule } from '@ember/runloop'; +import { inject as service } from '@ember/service'; +import { computed, observer } from '@ember/object'; +import Component from '@ember/component'; + +export default Component.extend({ + appMeta: service(), + sectionSvc: service('section'), + isDirty: false, + waiting: false, + diagramText: '', + diagramPreview: null, + previewButtonCaption: 'Preview', + + editorId: computed('page', function () { + let page = this.get('page'); + return `plantuml-editor-${page.id}`; + }), + previewId: computed('page', function () { + let page = this.get('page'); + return `plantuml-preview-${page.id}`; + }), + + generatePreview() { + this.set('waiting', true); + this.set('previewButtonCaption', 'Generating preview...'); + + let self = this; + let data = { data: this.get('diagramText') }; + + schedule('afterRender', () => { + this.get('sectionSvc').fetch(this.get('page'), 'preview', data).then(function (response) { + self.set('diagramPreview', response.data); + self.set('waiting', false); + self.set('previewButtonCaption', 'Preview'); + }, function (reason) { // eslint-disable-line no-unused-vars + self.set('diagramPreview', null); + self.set('waiting', false); + self.set('previewButtonCaption', 'Preview'); + }); + }); + }, + + didReceiveAttrs() { + this._super(...arguments); + this.set('waiting', false); + this.set('diagramText', this.get('meta.rawBody')); + + this.generatePreview(); + }, + + actions: { + isDirty() { + return this.get('isDirty') || (this.get('diagramText') !== this.get('meta.rawBody')); + }, + + onCancel() { + let cb = this.get('onCancel'); + cb(); + }, + + onPreview() { + this.generatePreview(); + }, + + onAction(title) { + this.set('waiting', true); + let page = this.get('page'); + let meta = this.get('meta'); + + meta.set('rawBody', this.get('diagramText')); + page.set('title', title); + + let cb = this.get('onAction'); + cb(page, meta); + this.set('waiting', false); + }, + + onInsertActivity() { + let txt = ` +@startuml +title Servlet Container + +(*) --> "ClickServlet.handleRequest()" +--> "new Page" + +if "Page.onSecurityCheck" then + ->[true] "Page.onInit()" + + if "isForward?" then + ->[no] "Process controls" + + if "continue processing?" then + -->[yes] ===RENDERING=== + else + -->[no] ===REDIRECT_CHECK=== + endif + + else + -->[yes] ===RENDERING=== + endif + + if "is Post?" then + -->[yes] "Page.onPost()" + --> "Page.onRender()" as render + --> ===REDIRECT_CHECK=== + else + -->[no] "Page.onGet()" + --> render + endif + +else + -->[false] ===REDIRECT_CHECK=== +endif + +if "Do redirect?" then + ->[yes] "redirect request" + --> ==BEFORE_DESTROY=== +else + if "Do Forward?" then + -left->[yes] "Forward request" + --> ==BEFORE_DESTROY=== + else + -right->[no] "Render page template" + --> ==BEFORE_DESTROY=== + endif +endif + +--> "Page.onDestroy()" +-->(*) +@enduml`; + + this.set('diagramText', txt); + this.generatePreview(); + }, + + onInsertSequence() { + let txt = ` +@startuml +actor Bob #red +' The only difference between actor +'and participant is the drawing +participant Alice +participant "I have a reallylong name" as L #99FF99 +/' You can also declare: + participant L as "I have a really long name" #99FF99 + '/ + +Alice->Bob: Authentication Request +Bob->Alice: Authentication Response +Bob->L: Log transaction +@enduml`; + + this.set('diagramText', txt); + this.generatePreview(); + }, + + onInsertUseCase() { + let txt = ` +@startuml +:Main Admin: as Admin +(Use the application) as (Use) + +User -> (Start) +User --> (Use) + +Admin ---> (Use) + +note right of Admin : This is an example. + +note right of (Use) + A note can also + be on several lines +end note + +note "This note is connected to several objects." as N2 +(Start) .. N2 +N2 .. (Use) +@enduml +`; + + this.set('diagramText', txt); + this.generatePreview(); + }, + + onInsertClass() { + let txt = ` +@startuml +class Foo1 { + You can use + several lines + .. + as you want + and group + == + things together. + __ + You can have as many groups + as you want + -- + End of class +} + +class User { + .. Simple Getter .. + + getName() + + getAddress() + .. Some setter .. + + setName() + __ private data __ + int age + -- encrypted -- + String password +} + +@enduml`; + + this.set('diagramText', txt); + this.generatePreview(); + }, + + onInsertActivityNew() { + let txt = ` +@startuml + +start +:ClickServlet.handleRequest(); +:new page; +if (Page.onSecurityCheck) then (true) + :Page.onInit(); + if (isForward?) then (no) + :Process controls; + if (continue processing?) then (no) + stop + endif + + if (isPost?) then (yes) + :Page.onPost(); + else (no) + :Page.onGet(); + endif + :Page.onRender(); + endif +else (false) +endif + +if (do redirect?) then (yes) + :redirect process; +else + if (do forward?) then (yes) + :Forward request; + else (no) + :Render page template; + endif +endif + +stop + +@enduml`; + + this.set('diagramText', txt); + this.generatePreview(); + }, + + onInsertComponent() { + let txt = ` +@startuml + +package "Some Group" { + HTTP - [First Component] + [Another Component] +} + +node "Other Groups" { + FTP - [Second Component] + [First Component] --> FTP +} + +cloud { + [Example 1] +} + + +database "MySql" { + folder "This is my folder" { + [Folder 3] + } + frame "Foo" { + [Frame 4] + } +} + + +[Another Component] --> [Example 1] +[Example 1] --> [Folder 3] +[Folder 3] --> [Frame 4] + +@enduml`; + + this.set('diagramText', txt); + this.generatePreview(); + }, + + onInsertState() { + let txt = ` +@startuml +scale 600 width + +[*] -> State1 +State1 --> State2 : Succeeded +State1 --> [*] : Aborted +State2 --> State3 : Succeeded +State2 --> [*] : Aborted +state State3 { + state "Some State Name" as long1 + long1 : Just a test + [*] --> long1 + long1 --> long1 : New Data + long1 --> ProcessData : Enough Data +} +State3 --> State3 : Failed +State3 --> [*] : Succeeded / Save Result +State3 --> [*] : Aborted + +@enduml`; + + this.set('diagramText', txt); + this.generatePreview(); + } + } +}); diff --git a/gui/app/components/section/mermaid/type-renderer.js b/gui/app/components/section/plantuml/type-renderer.js similarity index 100% rename from gui/app/components/section/mermaid/type-renderer.js rename to gui/app/components/section/plantuml/type-renderer.js diff --git a/gui/app/styles/app.scss b/gui/app/styles/app.scss index 3a94f533..946bba10 100644 --- a/gui/app/styles/app.scss +++ b/gui/app/styles/app.scss @@ -28,7 +28,7 @@ @import "section/markdown.scss"; @import "section/table.scss"; @import "section/code.scss"; -@import "section/mermaid.scss"; +@import "section/plantuml.scss"; @import "section/papertrail.scss"; @import "section/wysiwyg.scss"; diff --git a/gui/app/styles/section/mermaid.scss b/gui/app/styles/section/mermaid.scss deleted file mode 100644 index 254b2b05..00000000 --- a/gui/app/styles/section/mermaid.scss +++ /dev/null @@ -1,4 +0,0 @@ -.section-mermaid-diagram { - margin: 0; - padding: 0; -} diff --git a/gui/app/styles/section/plantuml.scss b/gui/app/styles/section/plantuml.scss new file mode 100644 index 00000000..8825c0dd --- /dev/null +++ b/gui/app/styles/section/plantuml.scss @@ -0,0 +1,4 @@ +.section-plantuml-diagram { + margin: 0; + padding: 0; +} diff --git a/gui/app/templates/components/section/base-editor.hbs b/gui/app/templates/components/section/base-editor.hbs index 4a280a44..57661a36 100644 --- a/gui/app/templates/components/section/base-editor.hbs +++ b/gui/app/templates/components/section/base-editor.hbs @@ -1,5 +1,4 @@
-
@@ -11,7 +10,6 @@
{{/if}}
-
{{#if busy}} @@ -23,7 +21,6 @@
-
@@ -31,7 +28,6 @@
-