2018-07-28 11:43:45 -04:00
|
|
|
// Copyright 2016 Documize Inc. <legal@documize.com>. 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 <sales@documize.com>.
|
|
|
|
//
|
|
|
|
// https://documize.com
|
|
|
|
|
|
|
|
package document
|
|
|
|
|
|
|
|
import (
|
2018-07-28 15:30:33 -04:00
|
|
|
"database/sql"
|
2018-07-28 11:43:45 -04:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/documize/community/domain"
|
2018-07-28 17:15:16 -04:00
|
|
|
"github.com/documize/community/domain/permission"
|
2018-09-27 15:14:48 +01:00
|
|
|
"github.com/documize/community/domain/store"
|
2018-07-28 17:15:16 -04:00
|
|
|
"github.com/documize/community/model/doc"
|
|
|
|
"github.com/documize/community/model/page"
|
2018-07-29 16:40:56 -04:00
|
|
|
pm "github.com/documize/community/model/permission"
|
2018-07-28 17:15:16 -04:00
|
|
|
"github.com/documize/community/model/workflow"
|
2018-07-28 11:43:45 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// exportSpec details what is being exported.
|
|
|
|
type exportSpec struct {
|
|
|
|
SpaceID string `json:"spaceId"`
|
|
|
|
FilterType string `json:"filterType"`
|
|
|
|
Data []string `json:"data"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// exportTOC details the list of documents being exported.
|
|
|
|
type exportTOC struct {
|
|
|
|
ID string
|
|
|
|
Entry string
|
|
|
|
}
|
|
|
|
|
|
|
|
// BuildExport generates self-enclosed HTML for content specified.
|
2018-09-27 15:14:48 +01:00
|
|
|
func BuildExport(ctx domain.RequestContext, s store.Store, spec exportSpec) (html string, err error) {
|
2018-07-28 11:43:45 -04:00
|
|
|
export := strings.Builder{}
|
2018-07-28 15:30:33 -04:00
|
|
|
content := strings.Builder{}
|
2018-07-28 11:43:45 -04:00
|
|
|
toc := []exportTOC{}
|
|
|
|
|
|
|
|
switch spec.FilterType {
|
|
|
|
case "space":
|
|
|
|
for _, spaceID := range spec.Data {
|
|
|
|
t, c, e := exportSpace(ctx, s, spaceID)
|
2018-07-28 15:47:14 -04:00
|
|
|
if e == nil {
|
|
|
|
content.WriteString(c)
|
|
|
|
toc = append(toc, t...)
|
2018-10-01 13:31:22 +01:00
|
|
|
} else {
|
|
|
|
fmt.Println("export.space", err)
|
2018-07-28 11:43:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case "category":
|
2018-07-28 17:15:16 -04:00
|
|
|
t, c, e := exportCategory(ctx, s, spec.SpaceID, spec.Data)
|
|
|
|
if e == nil {
|
|
|
|
content.WriteString(c)
|
|
|
|
toc = append(toc, t...)
|
2018-10-01 13:31:22 +01:00
|
|
|
} else {
|
|
|
|
fmt.Println("export.category", err)
|
2018-07-28 17:15:16 -04:00
|
|
|
}
|
|
|
|
|
2018-07-28 11:43:45 -04:00
|
|
|
case "document":
|
2018-07-28 17:15:16 -04:00
|
|
|
t, c, e := exportDocument(ctx, s, spec.SpaceID, spec.Data)
|
|
|
|
if e == nil {
|
|
|
|
content.WriteString(c)
|
|
|
|
toc = append(toc, t...)
|
2018-10-01 13:31:22 +01:00
|
|
|
} else {
|
|
|
|
fmt.Println("export.document", err)
|
2018-07-28 17:15:16 -04:00
|
|
|
}
|
2018-07-28 11:43:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate export file header.
|
|
|
|
export.WriteString("<!DOCTYPE html>")
|
|
|
|
export.WriteString("<html")
|
|
|
|
export.WriteString("<head>")
|
|
|
|
export.WriteString(`<meta charset="utf-8">`)
|
|
|
|
export.WriteString(`<meta http-equiv="X-UA-Compatible" content="IE=edge">`)
|
|
|
|
export.WriteString("<title>")
|
|
|
|
export.WriteString("Documize Export")
|
|
|
|
export.WriteString("</title>")
|
|
|
|
export.WriteString("<style>")
|
|
|
|
export.WriteString(baseCSS)
|
|
|
|
export.WriteString("</style>")
|
|
|
|
export.WriteString("<style>")
|
|
|
|
export.WriteString(exportCSS)
|
|
|
|
export.WriteString("</style>")
|
|
|
|
export.WriteString("</head>")
|
|
|
|
export.WriteString("<body class='export-body'>")
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Show title and timestamp.
|
2018-07-28 11:43:45 -04:00
|
|
|
generated := time.Now().UTC().Format(time.ANSIC)
|
|
|
|
export.WriteString(fmt.Sprintf("<h1 class='export-h1'>%s</h1>", "Documize Export"))
|
|
|
|
export.WriteString(fmt.Sprintf("<div class='export-stamp'>%v</div>", generated))
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Spit out table of contents.
|
2018-07-28 17:15:16 -04:00
|
|
|
if len(toc) > 0 {
|
|
|
|
export.WriteString("<div class='export-toc'>")
|
|
|
|
for i, t := range toc {
|
|
|
|
export.WriteString(fmt.Sprintf("<a class='export-toc-entry' href='#%s'>%d. %s</a>", t.ID, i+1, t.Entry))
|
|
|
|
}
|
|
|
|
export.WriteString("</div>")
|
|
|
|
} else {
|
|
|
|
export.WriteString("<p>No documents found</p>")
|
2018-07-28 15:30:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write out content.
|
|
|
|
export.WriteString(content.String())
|
2018-07-28 11:43:45 -04:00
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Generate export file footer.
|
|
|
|
export.WriteString("</body>")
|
|
|
|
export.WriteString("</html>")
|
2018-07-28 11:43:45 -04:00
|
|
|
|
|
|
|
return export.String(), nil
|
|
|
|
}
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// exportSpace returns documents exported.
|
2018-09-27 15:14:48 +01:00
|
|
|
func exportSpace(ctx domain.RequestContext, s store.Store, spaceID string) (toc []exportTOC, export string, err error) {
|
2018-07-28 15:30:33 -04:00
|
|
|
// Permission check.
|
|
|
|
if !permission.CanViewSpace(ctx, s, spaceID) {
|
|
|
|
return toc, "", nil
|
|
|
|
}
|
|
|
|
|
2018-07-29 16:40:56 -04:00
|
|
|
// Get space.
|
|
|
|
space, err := s.Space.Get(ctx, spaceID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return toc, export, err
|
|
|
|
}
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Get all documents for space.
|
|
|
|
docs, err := s.Document.GetBySpace(ctx, spaceID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return toc, export, err
|
|
|
|
}
|
|
|
|
|
2018-07-29 16:40:56 -04:00
|
|
|
// Can user view drafts?
|
|
|
|
// If space defaults to draft documents, then this means
|
|
|
|
// user can view drafts as long as they have edit rights.
|
|
|
|
viewDrafts := permission.CanViewDrafts(ctx, s, spaceID)
|
|
|
|
if space.Lifecycle == workflow.LifecycleDraft && permission.HasPermission(ctx, s, spaceID, pm.DocumentEdit) {
|
|
|
|
viewDrafts = true
|
|
|
|
}
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Remove documents that cannot be seen due to lack of category view/access permission.
|
|
|
|
cats, err := s.Category.GetBySpace(ctx, spaceID)
|
|
|
|
members, err := s.Category.GetSpaceCategoryMembership(ctx, spaceID)
|
2018-07-29 16:40:56 -04:00
|
|
|
docs = FilterCategoryProtected(docs, cats, members, viewDrafts)
|
2018-07-28 15:30:33 -04:00
|
|
|
|
|
|
|
// Keep the latest version when faced with multiple versions.
|
|
|
|
docs = FilterLastVersion(docs)
|
|
|
|
|
|
|
|
// Turn each document into TOC entry and HTML content export
|
|
|
|
b := strings.Builder{}
|
|
|
|
for _, d := range docs {
|
2018-07-29 16:40:56 -04:00
|
|
|
docHTML, e := processDocument(ctx, s, d.RefID)
|
|
|
|
if e == nil && len(docHTML) > 0 {
|
2018-09-19 16:03:29 +01:00
|
|
|
toc = append(toc, exportTOC{ID: d.RefID, Entry: d.Name})
|
2018-07-29 16:40:56 -04:00
|
|
|
b.WriteString(docHTML)
|
|
|
|
} else {
|
|
|
|
return toc, b.String(), err
|
2018-07-28 15:30:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return toc, b.String(), nil
|
|
|
|
}
|
|
|
|
|
2018-07-28 17:15:16 -04:00
|
|
|
// exportCategory returns documents exported for selected categories.
|
2018-09-27 15:14:48 +01:00
|
|
|
func exportCategory(ctx domain.RequestContext, s store.Store, spaceID string, category []string) (toc []exportTOC, export string, err error) {
|
2018-07-28 17:15:16 -04:00
|
|
|
// Permission check.
|
|
|
|
if !permission.CanViewSpace(ctx, s, spaceID) {
|
|
|
|
return toc, "", nil
|
|
|
|
}
|
|
|
|
|
2018-07-29 16:40:56 -04:00
|
|
|
// Get space.
|
|
|
|
space, err := s.Space.Get(ctx, spaceID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return toc, export, err
|
|
|
|
}
|
|
|
|
|
2018-07-28 17:15:16 -04:00
|
|
|
// Get all documents for space.
|
|
|
|
docs, err := s.Document.GetBySpace(ctx, spaceID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return toc, export, err
|
|
|
|
}
|
|
|
|
|
2018-07-29 16:40:56 -04:00
|
|
|
// Can user view drafts?
|
|
|
|
// If space defaults to draft documents, then this means
|
|
|
|
// user can view drafts as long as they have edit rights.
|
|
|
|
viewDrafts := permission.CanViewDrafts(ctx, s, spaceID)
|
|
|
|
if space.Lifecycle == workflow.LifecycleDraft && permission.HasPermission(ctx, s, spaceID, pm.DocumentEdit) {
|
|
|
|
viewDrafts = true
|
|
|
|
}
|
|
|
|
|
2018-07-28 17:15:16 -04:00
|
|
|
// Remove documents that cannot be seen due to lack of category view/access permission.
|
|
|
|
cats, err := s.Category.GetBySpace(ctx, spaceID)
|
|
|
|
members, err := s.Category.GetSpaceCategoryMembership(ctx, spaceID)
|
2018-07-29 16:40:56 -04:00
|
|
|
docs = FilterCategoryProtected(docs, cats, members, viewDrafts)
|
2018-07-28 17:15:16 -04:00
|
|
|
|
|
|
|
// Keep the latest version when faced with multiple versions.
|
|
|
|
docs = FilterLastVersion(docs)
|
|
|
|
|
|
|
|
exportDocs := []doc.Document{}
|
|
|
|
|
|
|
|
// Process each requested category.
|
|
|
|
for _, categoryID := range category {
|
|
|
|
// Check to see if any documents has this category
|
|
|
|
for _, cm := range members {
|
|
|
|
// Save the document for export if it is in list of visible docs.
|
|
|
|
if cm.CategoryID == categoryID {
|
|
|
|
for _, d := range docs {
|
|
|
|
if d.RefID == cm.DocumentID {
|
|
|
|
exportDocs = append(exportDocs, d)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn each document into TOC entry and HTML content export
|
|
|
|
b := strings.Builder{}
|
|
|
|
for _, d := range exportDocs {
|
2018-07-29 16:40:56 -04:00
|
|
|
docHTML, e := processDocument(ctx, s, d.RefID)
|
|
|
|
if e == nil && len(docHTML) > 0 {
|
2018-09-19 16:03:29 +01:00
|
|
|
toc = append(toc, exportTOC{ID: d.RefID, Entry: d.Name})
|
2018-07-29 16:40:56 -04:00
|
|
|
b.WriteString(docHTML)
|
|
|
|
} else {
|
|
|
|
return toc, b.String(), err
|
2018-07-28 17:15:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return toc, b.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// exportDocument returns documents for export.
|
2018-09-27 15:14:48 +01:00
|
|
|
func exportDocument(ctx domain.RequestContext, s store.Store, spaceID string, document []string) (toc []exportTOC, export string, err error) {
|
2018-07-28 17:15:16 -04:00
|
|
|
// Permission check.
|
|
|
|
if !permission.CanViewSpace(ctx, s, spaceID) {
|
|
|
|
return toc, "", nil
|
|
|
|
}
|
|
|
|
|
2018-07-29 16:40:56 -04:00
|
|
|
// Get space.
|
|
|
|
space, err := s.Space.Get(ctx, spaceID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return toc, export, err
|
|
|
|
}
|
|
|
|
|
2018-07-28 17:15:16 -04:00
|
|
|
// Get all documents for space.
|
|
|
|
docs, err := s.Document.GetBySpace(ctx, spaceID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return toc, export, err
|
|
|
|
}
|
|
|
|
|
2018-07-29 16:40:56 -04:00
|
|
|
// Can user view drafts?
|
|
|
|
// If space defaults to draft documents, then this means
|
|
|
|
// user can view drafts as long as they have edit rights.
|
|
|
|
viewDrafts := permission.CanViewDrafts(ctx, s, spaceID)
|
|
|
|
if space.Lifecycle == workflow.LifecycleDraft && permission.HasPermission(ctx, s, spaceID, pm.DocumentEdit) {
|
|
|
|
viewDrafts = true
|
|
|
|
}
|
|
|
|
|
2018-07-28 17:15:16 -04:00
|
|
|
// Remove documents that cannot be seen due to lack of category view/access permission.
|
|
|
|
cats, err := s.Category.GetBySpace(ctx, spaceID)
|
|
|
|
members, err := s.Category.GetSpaceCategoryMembership(ctx, spaceID)
|
2018-07-29 16:40:56 -04:00
|
|
|
docs = FilterCategoryProtected(docs, cats, members, viewDrafts)
|
2018-07-28 17:15:16 -04:00
|
|
|
|
|
|
|
// Keep the latest version when faced with multiple versions.
|
|
|
|
docs = FilterLastVersion(docs)
|
|
|
|
|
|
|
|
// Turn each document into TOC entry and HTML content export
|
|
|
|
b := strings.Builder{}
|
|
|
|
for _, documentID := range document {
|
|
|
|
for _, d := range docs {
|
2018-07-29 10:59:24 -04:00
|
|
|
if d.RefID == documentID {
|
|
|
|
if permission.CanViewDocument(ctx, s, d.RefID) {
|
|
|
|
docHTML, e := processDocument(ctx, s, d.RefID)
|
|
|
|
if e == nil && len(docHTML) > 0 {
|
2018-09-19 16:03:29 +01:00
|
|
|
toc = append(toc, exportTOC{ID: d.RefID, Entry: d.Name})
|
2018-07-29 10:59:24 -04:00
|
|
|
b.WriteString(docHTML)
|
|
|
|
} else {
|
|
|
|
return toc, b.String(), err
|
|
|
|
}
|
2018-07-28 17:15:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return toc, b.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// processDocument writes out document as HTML content
|
2018-09-27 15:14:48 +01:00
|
|
|
func processDocument(ctx domain.RequestContext, s store.Store, documentID string) (export string, err error) {
|
2018-07-28 15:30:33 -04:00
|
|
|
b := strings.Builder{}
|
|
|
|
|
|
|
|
// Permission check.
|
|
|
|
if !permission.CanViewDocument(ctx, s, documentID) {
|
|
|
|
return export, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the document in question
|
|
|
|
doc, err := s.Document.Get(ctx, documentID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return export, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get published pages and new pages awaiting approval.
|
|
|
|
pages, err := s.Page.GetPages(ctx, documentID)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return export, err
|
|
|
|
}
|
|
|
|
if len(pages) == 0 {
|
|
|
|
pages = []page.Page{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only show published pages
|
|
|
|
p := []page.Page{}
|
|
|
|
for _, page := range pages {
|
|
|
|
if page.Status == workflow.ChangePublished {
|
|
|
|
p = append(p, page)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attach section numbers
|
|
|
|
page.Numberize(p)
|
|
|
|
|
|
|
|
// Put out document name.
|
2018-07-28 15:47:14 -04:00
|
|
|
b.WriteString(fmt.Sprintf("<div class='export-doc-header' id='%s'>", doc.RefID))
|
|
|
|
b.WriteString("<div class='export-doc-title'>")
|
2018-09-19 16:03:29 +01:00
|
|
|
b.WriteString(doc.Name)
|
2018-07-28 15:47:14 -04:00
|
|
|
b.WriteString("</div>")
|
|
|
|
b.WriteString("<div class='export-doc-excerpt'>")
|
2018-07-28 15:30:33 -04:00
|
|
|
b.WriteString(doc.Excerpt)
|
2018-07-28 15:47:14 -04:00
|
|
|
b.WriteString("</div>")
|
|
|
|
b.WriteString("</div>")
|
2018-07-28 15:30:33 -04:00
|
|
|
|
|
|
|
// Construct HMTL.
|
|
|
|
for _, page := range p {
|
|
|
|
// Write out section header.
|
2019-01-08 18:11:48 +00:00
|
|
|
b.WriteString(`<div class="section-heading">`)
|
2018-07-28 15:30:33 -04:00
|
|
|
b.WriteString(`<div class="page-header">`)
|
|
|
|
b.WriteString(fmt.Sprintf("<span class='page-number'>%s</span>", page.Numbering))
|
2018-09-19 16:03:29 +01:00
|
|
|
b.WriteString(fmt.Sprintf("<span class='page-title'>%s</span>", page.Name))
|
2018-07-28 15:30:33 -04:00
|
|
|
b.WriteString("</div>")
|
|
|
|
b.WriteString("</div>")
|
|
|
|
|
2018-07-29 14:08:39 -04:00
|
|
|
// Process seciton content before writing out as HTML.
|
|
|
|
section := page.Body
|
2018-07-29 16:40:56 -04:00
|
|
|
if page.ContentType == "plantuml" || page.ContentType == "flowchart" {
|
2018-07-29 14:08:39 -04:00
|
|
|
section = fmt.Sprintf(`<img src="%s" />`, page.Body)
|
|
|
|
}
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Write out section content
|
|
|
|
b.WriteString(`<div class="wysiwyg">`)
|
2018-07-29 14:08:39 -04:00
|
|
|
b.WriteString(section)
|
2018-07-28 15:30:33 -04:00
|
|
|
b.WriteString("</div>")
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.String(), nil
|
2018-07-28 11:43:45 -04:00
|
|
|
}
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// CSS injected into self-enclosed HTML file export.
|
2018-07-28 11:43:45 -04:00
|
|
|
const (
|
2018-07-28 15:30:33 -04:00
|
|
|
// Styles specific to export process.
|
2018-07-28 11:43:45 -04:00
|
|
|
exportCSS = `
|
|
|
|
.export-body {
|
|
|
|
margin: 30px 20px;
|
|
|
|
}
|
|
|
|
.export-h1 {
|
2019-01-08 18:11:48 +00:00
|
|
|
color: #0E311D;
|
2018-07-28 11:43:45 -04:00
|
|
|
font-size: 2rem;
|
|
|
|
font-weight: bold;
|
|
|
|
margin: 0 0 10px 0;
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
.export-stamp {
|
2019-01-08 18:11:48 +00:00
|
|
|
color: #164B2C;
|
2018-07-28 11:43:45 -04:00
|
|
|
font-size: 1.2rem;
|
|
|
|
font-weight: normal;
|
|
|
|
margin: 0 0 20px 0;
|
|
|
|
}
|
2018-07-28 15:30:33 -04:00
|
|
|
.export-toc {
|
|
|
|
padding: 20px 30px;
|
|
|
|
margin: 10px 0;
|
2019-01-08 18:11:48 +00:00
|
|
|
background-color: #FCF0D3;
|
|
|
|
border: 2px solid #F7DA94;
|
2018-07-28 15:30:33 -04:00
|
|
|
border-radius: 3px;
|
|
|
|
}
|
|
|
|
.export-toc .export-toc-entry {
|
|
|
|
display: block;
|
|
|
|
margin: 3px 0;
|
2019-01-08 18:11:48 +00:00
|
|
|
color: #936B0A;
|
2018-07-28 15:30:33 -04:00
|
|
|
font-size: 1.1rem;
|
|
|
|
text-decoration: none;
|
|
|
|
}
|
|
|
|
.export-toc .export-toc-entry:hover {
|
|
|
|
text-decoration: underline;
|
|
|
|
}
|
2018-07-28 15:47:14 -04:00
|
|
|
.export-doc-header {
|
2018-07-28 15:30:33 -04:00
|
|
|
padding: 20px 30px;
|
2018-07-28 15:47:14 -04:00
|
|
|
margin: 70px 0 30px 0;
|
2019-01-08 18:11:48 +00:00
|
|
|
background-color: #D5DDE5;
|
|
|
|
border: 2px solid #CBD4DB;
|
2018-07-28 15:30:33 -04:00
|
|
|
border-radius: 3px;
|
|
|
|
}
|
2018-07-28 15:47:14 -04:00
|
|
|
.export-doc-title {
|
2019-01-08 18:11:48 +00:00
|
|
|
color: #404B5A;
|
2018-07-28 15:30:33 -04:00
|
|
|
font-size: 2rem;
|
|
|
|
font-weight; bold;
|
|
|
|
margin: 0 0 5px 0;
|
|
|
|
}
|
2018-07-28 15:47:14 -04:00
|
|
|
.export-doc-excerpt {
|
2019-01-08 18:11:48 +00:00
|
|
|
color: #6E7A89;
|
2018-07-28 15:30:33 -04:00
|
|
|
font-size: 1.1rem;
|
|
|
|
font-weight; normal;
|
|
|
|
}
|
2019-06-25 17:25:00 +01:00
|
|
|
|
|
|
|
.section-heading > .page-header {
|
|
|
|
margin: 2rem 0 2rem 0 !important;
|
|
|
|
}
|
2018-07-28 11:43:45 -04:00
|
|
|
`
|
|
|
|
|
2018-07-28 15:30:33 -04:00
|
|
|
// Styles copied from minified production CSS assets.
|
2019-06-25 17:25:00 +01:00
|
|
|
// It's VENDOR.CSS followed by DOCUMIZE.CSS.
|
2018-07-28 11:43:45 -04:00
|
|
|
baseCSS = `
|
2019-06-03 12:21:18 +01:00
|
|
|
.x-toggle{display:none}.x-toggle,.x-toggle *,.x-toggle ::after,.x-toggle ::before,.x-toggle+label>.x-toggle-btn,.x-toggle::after,.x-toggle::before{-moz-box-sizing:border-box;box-sizing:border-box}.x-toggle ::-moz-selection,.x-toggle ::after::-moz-selection,.x-toggle ::before::-moz-selection,.x-toggle+label>.x-toggle-btn::-moz-selection,.x-toggle::-moz-selection,.x-toggle::after::-moz-selection,.x-toggle::before::-moz-selection{background:0 0}.x-toggle ::after::selection,.x-toggle ::before::selection,.x-toggle ::selection,.x-toggle+label>.x-toggle-btn::selection,.x-toggle::after::selection,.x-toggle::before::selection,.x-toggle::selection{background:0 0}label>.x-toggle-btn.x-toggle-disabled{cursor:not-allowed}label>.x-toggle-btn{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:0;display:flex;flex-basis:4em;height:2em;position:relative;cursor:pointer}label>.x-toggle-btn::after,label>.x-toggle-btn::before{position:relative;display:block;content:"";width:50%;height:100%}label>.x-toggle-btn::after{left:0}label>.x-toggle-btn::before{display:none}.x-toggle:checked+label>.x-toggle-btn::after{left:50%}.x-toggle-component{display:flex;justify-content:center;align-items:center;outline:0}.x-toggle-component.x-toggle-focused .x-toggle-btn:not(.x-toggle-disabled)::after,.x-toggle-component.x-toggle-focused .x-toggle-btn:not(.x-toggle-disabled)::before{-webkit-box-shadow:0 0 3px 2px #0099e0;-moz-box-shadow:0 0 3px 2px #0099e0;box-shadow:0 0 2px 3px #0099e0}.x-toggle-container{display:flex;flex-wrap:nowrap;justify-content:center;height:auto;padding:0 .35rem}.x-toggle-component .toggle-text{display:flex;cursor:pointer}.x-toggle-container.small{width:2.75rem;font-size:1rem;padding:0 .25rem}.x-toggle-container.medium{width:3.75rem;font-size:1rem}.x-toggle-container.large{width:5.7rem;font-size:1.2rem;padding:0 .5rem}.x-toggle-container label{min-width:100%;max-width:100%}.x-toggle-container .toggle-text.toggle-prefix{padding-right:.25rem}.x-toggle-container .toggle-text.toggle-postfix{padding-left:.25rem}.x-toggle-component label.off-label{padding-right:.5rem}.x-toggle-component label.on-label{padding-left:.5rem}.x-toggle-light.x-toggle-btn{background:#f0f0f0;border-radius:2em;padding:2px;-webkit-transition:all .4s ease;transition:all .4s ease}.x-toggle-light.x-toggle-btn::after{border-radius:50%;background:#fff;-webkit-transition:all .2s ease;transition:all .2s ease}.x-toggle:checked+label>.x-toggle-light.x-toggle-btn{background:#9fd6ae}.x-toggle-light.small{width:3em;height:1.6em}.x-toggle-light.medium{width:4em;height:2.1em;padding:3px}.x-toggle-light.large{width:4.7em;height:2.1em;padding:4px}.x-toggle-ios.x-toggle-btn{background:#fbfbfb;border-radius:2em;padding:2px;-webkit-transition:all .4s ease;transition:all .4s ease;border:1px solid #e8eae9}.x-toggle-ios.x-toggle-btn::after{border-radius:2em;background:#fbfbfb;-webkit-transition:left .3s cubic-bezier(.175,.885,.32,1.275),padding .3s ease,margin .3s ease;transition:left .3s cubic-bezier(.175,.885,.32,1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.x-toggle-ios.x-toggle-btn:active{box-shadow:inset 0 0 0 2em #e8eae9}.x-toggle-ios.x-toggle-btn:active::after{padding-right:.8em}.x-toggle:checked+label>.x-toggle-ios.x-toggle-btn{background:#86d993}.x-toggle:checked+label>.x-toggle-ios.x-toggle-btn:active{box-shadow:none}.x-toggle:checked+label>.x-toggle-ios.x-toggle-btn:active::after{margin-left:-.8em}.x-toggle-ios.small{width:3em;height:1.6em}.x-toggle-ios.medium{width:4em;height:2.1em;padding:3px}.x-toggle-ios.large{width:4.7em;height:2.1em;padding:4px}.x-toggle-flip.x-toggle-btn{padding:2px;-webkit-transition:all .2s ease;transition:all .2s ease;font-family:sans-serif;-webkit-perspective:100px;perspective:100px}.x-toggle-flip.x-toggle-btn::after,.x-toggle-flip.x-toggle-btn::before{display:inline-block;-webkit-transition:all .4s ease;transition:all .4s ease;width:100%;text-align:center;position:absolute;line-height:2em;font-weight:700;color:#fff;top:0;left:0;-webkit-backface-visibility:hi
|
|
|
|
.material-icons,html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}body,html{height:100%}.dicon,.dmeta{text-transform:none;-moz-osx-font-smoothing:grayscale}.CodeMirror pre,html{-webkit-tap-highlight-color:transparent}.color-gray-900{color:#1F2833}.background-color-gray-900{background-color:#1F2833}.color-gray-800{color:#404B5A}.background-color-gray-800{background-color:#404B5A}.color-gray-700{color:#6E7A89}.background-color-gray-700{background-color:#6E7A89}.color-gray-600{color:#929FB1}.background-color-gray-600{background-color:#929FB1}.color-gray-500{color:#AEBECC}.background-color-gray-500{background-color:#AEBECC}.color-gray-400{color:#CBD4DB}.background-color-gray-400{background-color:#CBD4DB}.color-gray-300{color:#D5DDE5}.background-color-gray-300{background-color:#D5DDE5}.color-gray-200{color:#E1E7EB}.background-color-gray-200{background-color:#E1E7EB}.color-gray-100{color:#F8F9FA}.background-color-gray-100{background-color:#F8F9FA}.color-red-900{color:#431010}.background-color-red-900{background-color:#431010}.color-red-800{color:#621111}.background-color-red-800{background-color:#621111}.color-red-700{color:#8E1818}.background-color-red-700{background-color:#8E1818}.color-red-600{color:#AB1D1D}.background-color-red-600{background-color:#AB1D1D}.color-red-500{color:#D72424}.background-color-red-500{background-color:#D72424}.color-red-400{color:#E14D4D}.background-color-red-400{background-color:#E14D4D}.color-red-300{color:#E66A6A}.background-color-red-300{background-color:#E66A6A}.color-red-200{color:#F2B3B3}.background-color-red-200{background-color:#F2B3B3}.color-red-100{color:#FCEDED}.background-color-red-100{background-color:#FCEDED}.color-yellow-900{color:#433105}.background-color-yellow-900{background-color:#433105}.color-yellow-800{color:#936B0A}.background-color-yellow-800{background-color:#936B0A}.color-yellow-700{color:#C28E0E}.background-color-yellow-700{background-color:#C28E0E}.color-yellow-600{color:#EEAF15}.background-color-yellow-600{background-color:#EEAF15}.background-color-yellow-500,.document-meta>.label-draft{background-color:#F4CA64}.color-yellow-500{color:#F4CA64}.color-yellow-400{color:#F7DA94}.background-color-yellow-400{background-color:#F7DA94}.color-yellow-300{color:#FAE5B3}.background-color-yellow-300{background-color:#FAE5B3}.color-yellow-200{color:#FCF0D3}.background-color-yellow-200{background-color:#FCF0D3}.color-yellow-100{color:#FDF5E3}.background-color-yellow-100{background-color:#FDF5E3}.color-green-900{color:#0E311D}.background-color-green-900{background-color:#0E311D}.color-green-800{color:#164B2C}.background-color-green-800{background-color:#164B2C}.color-green-700{color:#227243}.background-color-green-700{background-color:#227243}.color-green-600{color:#2D9A5A}.background-color-green-600{background-color:#2D9A5A}.color-green-500{color:#39C171}.background-color-green-500{background-color:#39C171}.color-green-400{color:#6BD396}.background-color-green-400{background-color:#6BD396}.color-green-300{color:#92DFB2}.background-color-green-300{background-color:#92DFB2}.color-green-200{color:#B9EACE}.background-color-green-200{background-color:#B9EACE}.color-green-100{color:#E1F6EA}.background-color-green-100{background-color:#E1F6EA}.color-black{color:#000}.color-black-light-1{color:#1c1c1c}.color-black-light-2{color:#252525}.color-black-light-3{color:#434343}.background-color-black{background-color:#000}.background-color-black-light-1,.user-notification{background-color:#1c1c1c}.background-color-black-light-2{background-color:#252525}.background-color-black-light-3{background-color:#434343}.color-white{color:#fff}.color-white-dark-1{color:#f5f5f5}.background-color-white{background-color:#fff}.background-color-white-dark-1{background-color:#f5f5f5}.color-theme-900{color:#160624}.color-theme-800{color:#1F0833}.color-theme-700{color:#310C51}.color-theme-600{color:#4C137D}.color-theme-500{color:#43116E}.color-theme-400{color:#9237E1}.color-theme-300{color:#CA9EF0}.color-theme-200{color:#EAD9F9}.color-theme-100{color:#FBF7FE}.background-color-theme-900{backgr
|
2019-01-08 18:11:48 +00:00
|
|
|
* animate.css -http://daneden.me/animate
|
|
|
|
* Version - 3.7.0
|
|
|
|
* Licensed under the MIT license - http://opensource.org/licenses/MIT
|
|
|
|
*
|
|
|
|
* Copyright (c) 2018 Daniel Eden
|
2019-06-03 12:21:18 +01:00
|
|
|
*/@-webkit-keyframes bounce{20%,53%,80%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{20%,53%,80%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{50%,from,to{opacity:1}25%,75%{opacity:0}}@keyframes flash{50%,from,to{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{from,to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}}@keyframes pulse{from,to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{from,to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}}@keyframes rubberBand{from,to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{from,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{from,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-
|
2019-01-08 18:11:48 +00:00
|
|
|
font-size: 12px;
|
2019-06-03 12:21:18 +01:00
|
|
|
color: #1c1c1c; }
|
2019-01-08 18:11:48 +00:00
|
|
|
@bottom-left {
|
|
|
|
font-size: 12px;
|
2019-06-03 12:21:18 +01:00
|
|
|
color: #1c1c1c; }
|
2019-01-08 18:11:48 +00:00
|
|
|
@bottom-right {
|
|
|
|
font-size: 12px;
|
2019-06-03 12:21:18 +01:00
|
|
|
color: #1c1c1c; };}body,html{background-color:transparent!important;max-width:none!important;float:none!important;position:relative!important;height:initial!important;min-height:initial!important;-webkit-print-color-adjust:exact;overflow:hidden;margin:0!important;padding:0!important;min-width:768px!important}.master-sidebar-container .page-toolbar,.new-section-wizard,.no-print,.non-printable,.start-section{float:none!important;display:none!important;margin:0!important;padding:0!important;width:0!important;z-index:0!important}.master-content,.non-printable-message,.print-title{display:block!important}.document-heading{text-align:left!important;padding:0!important;margin-bottom:40px!important;color:#000}.doc-excerpt,.doc-title,.master-content{padding:0!important;margin:0!important}.doc-title{font-size:1.5rem!important;color:#000!important}.doc-excerpt{color:#464545!important;font-size:1.2rem!important}.page-title,.wysiwyg,.wysiwyg h1,.wysiwyg h2,.wysiwyg h3,.wysiwyg h4,.wysiwyg h5,.wysiwyg h6{color:#000!important}.wysiwyg{font-size:15px!important;line-height:22px!important}.wysiwyg h1{font-size:22px}.wysiwyg h2{font-size:20px}.wysiwyg h3{font-size:18px}.wysiwyg h4{font-size:17px}.wysiwyg h5,.wysiwyg h6{font-size:16px}.page-title{font-size:20px!important}.master-content{flex:none!important;width:auto!important;max-width:auto!important}}.product-update{text-align:left}.product-update>.update-status{padding:25px;background-color:#FDF5E3;border:1px solid #FAE5B3;border-radius:3px}.product-update>.update-status>.title{font-weight:700;font-size:1.5rem;color:#936B0A;margin-bottom:5px;display:block}.product-update>.update-status>.instructions{font-weight:500;font-size:1.1rem;color:#404B5A;margin-bottom:30px}.product-update>.update-status>.links{margin:10px 0 0}.product-update>.update-status>.links>p{margin:.3rem 0}.product-update>.update-status>.links>p>.edition-name{font-size:1.1rem;font-weight:400;color:#433105}.product-update>.update-status>.links>p>.link{font-size:1.1rem;color:#EEAF15;font-weight:600}.product-update>.change-log{padding:25px;background-color:#F8F9FA;border:1px solid #E1E7EB;border-radius:3px}.product-update>.change-log>.version{font-weight:700;font-size:1.2rem;color:#6E7A89;margin:10px 0;display:block}.product-update>.change-log>.changes{margin:10px 0 0 40px}.product-update>.change-log>.changes>li{list-style-type:disc;padding:5px 0;font-size:1.2rem;color:#000}.product-update>.change-log>.changes>li>.tag-edition{margin:10px;padding:5px 10px;background-color:#B9EACE;color:#2D9A5A;font-weight:700;font-size:.9rem;border-radius:3px}.product-about{text-align:center;margin:30px}.product-about>.edition{font-weight:400;font-size:1.5rem;color:#000;margin-bottom:5px}.product-about>.version{font-weight:700;font-size:1.1rem;color:#929FB1;margin-bottom:20px}.product-about>.dotcom{font-weight:700;font-size:1.2rem;color:#0080a5;margin-bottom:40px}.product-about>.copyright{text-align:center;font-weight:400;font-size:1rem;color:#1c1c1c;margin-bottom:20px}.product-about>.license{text-align:left;font-weight:400;font-size:1rem;color:#929FB1}.update-available-dot{border-radius:10px;width:10px;height:10px;position:absolute;bottom:0;right:0}.whats-new-dot{border-radius:10px;width:10px;height:10px;background-color:#AB1D1D;position:absolute;top:0;right:0}.product-news{text-align:left;margin:0 30px}.product-news>h2{margin:0 0 10px;text-align:center;font-size:2rem;color:#1c1c1c}.product-news>.news-item{padding:30px 0;text-align:center}.product-news>.news-item>.title{color:#43116E;font-size:1.5rem;font-weight:700;margin-bottom:5px}.product-news>.news-item>.date{color:#929FB1;font-size:1rem;font-weight:600;margin-bottom:10px}.product-news>.news-item>.info{color:#000;font-size:1.1rem;font-weight:400;margin-top:15px}.product-news>.news-item>.tag-edition{margin:10px;padding:5px 10px;background-color:#F8F9FA;color:#929FB1;font-weight:700;font-size:.9rem;display:inline-block}.product-news>.news-item>img{max-width:450px;max-height:350px}.product-news>.action{margin:20px 0;text-align:center;color:#929FB1;font-weight:800;font-size:1.3rem}.cm
|
2019-01-12 15:14:55 +00:00
|
|
|
`
|
2018-07-28 11:43:45 -04:00
|
|
|
)
|