Merge pull request #6445 from opf/ckeditor-integration

Ckeditor integration branch

[ci skip]
pull/6449/head
Oliver Günther 6 years ago committed by GitHub
commit 0225163900
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/assets/javascripts/vendor/ckeditor/ckeditor.js
  2. 2
      app/assets/javascripts/vendor/ckeditor/ckeditor.js.map
  3. 43
      app/helpers/text_formatting_helper.rb
  4. 6
      app/views/messages/_form.html.erb
  5. 4
      app/views/news/_form.html.erb
  6. 2
      app/views/news/show.html.erb
  7. 2
      app/views/wiki/_page_form.html.erb
  8. 2
      frontend/src/app/components/ckeditor/ckeditor-setup.service.ts
  9. 6
      frontend/src/app/components/ckeditor/op-ckeditor-form.component.ts
  10. 3
      frontend/src/app/components/modals/editor/editor-macros.service.ts
  11. 2
      frontend/src/app/modules/common/path-helper/apiv3/apiv3-paths.ts
  12. 15
      frontend/src/app/modules/fields/edit/field-types/formattable-edit-field.ts
  13. 16
      lib/api/v3/render/render_api.rb
  14. 2
      lib/open_project/text_formatting/formats/markdown/helper.rb
  15. 3
      lib/tabular_form_builder.rb
  16. 13
      spec/features/wysiwyg/macros/child_pages_spec.rb
  17. 7
      spec/features/wysiwyg/macros/include_wiki_page_spec.rb

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -34,16 +34,13 @@ module TextFormattingHelper
:text_formatting_js_includes,
:wikitoolbar_for
def preview_context(object = nil)
paths = API::V3::Utilities::PathHelper::ApiV3Path
case object
when News
paths.news(object.id)
when Message
paths.post(object.id)
when WikiPage
paths.wiki_page(object.id)
def preview_context(object, project = nil)
if object.new_record?
project_preview_context(object, project)
elsif object.is_a? Message
message_preview_context(object)
else
object_preview_context(object)
end
end
@ -52,4 +49,30 @@ module TextFormattingHelper
helper_class = OpenProject::TextFormatting::Formats.rich_helper
helper_class.new(self)
end
def project_preview_context(object, project)
relevant_project = if project
project
elsif object.respond_to?(:project) && object.project
object.project
end
return nil unless relevant_project
API::V3::Utilities::PathHelper::ApiV3Path
.project(relevant_project.id)
end
def message_preview_context(message)
API::V3::Utilities::PathHelper::ApiV3Path
.post(message.id)
end
def object_preview_context(object)
paths = API::V3::Utilities::PathHelper::ApiV3Path
if paths.respond_to?(object.class.name.underscore.singularize)
paths.send(object.class.name.underscore.singularize, object.id)
end
end
end

@ -47,10 +47,8 @@ See docs/COPYRIGHT.rdoc for more details.
<div class="form--field -required">
<% preview_object = if replying
f.object.parent
elsif f.object.persisted?
f.object
else
nil
f.object
end %>
<%= f.text_area :content,
@ -59,6 +57,6 @@ See docs/COPYRIGHT.rdoc for more details.
class: 'wiki-edit',
container_class: '-wide',
with_text_formatting: true,
preview_context: preview_context(preview_object) %>
preview_context: preview_context(preview_object, @project) %>
</div>
<%= render partial: 'attachments/form' %>

@ -38,11 +38,9 @@ See docs/COPYRIGHT.rdoc for more details.
container_class: '-wide' %>
</div>
<div class="form--field">
<% link_object = f.object.persisted? ? f.object : nil %>
<%= f.text_area :description,
required: true,
class: 'wiki-edit wiki-toolbar',
container_class: '-wide',
with_text_formatting: true,
preview_context: preview_context(link_object) %>
with_text_formatting: true %>
</div>

@ -97,7 +97,7 @@ See docs/COPYRIGHT.rdoc for more details.
<%= text_area 'comment',
'comments',
class: 'wiki-edit' %>
<%= wikitoolbar_for 'comment_comments' %>
<%= wikitoolbar_for 'comment_comments', preview_context: preview_context(@news) %>
</div>
<p><%= submit_tag l(:button_add), class: 'button -highlight' %></p>
<% end %>

@ -27,7 +27,7 @@
accesskey: accesskey(:edit),
with_text_formatting: true,
resource: ::API::V3::WikiPages::WikiPageRepresenter.new(@page, current_user: current_user),
preview_context: preview_context(@page) %>
preview_context: preview_context(@page, @project) %>
</div>
<div class="form--field">

@ -20,6 +20,8 @@ export interface ICKEditorContext {
removePlugins?:string[];
// Set of enabled macro plugins or false to disable all
macros?:false|string[];
// context link to append on preview requests
previewContext?:string;
}
declare global {

@ -84,9 +84,13 @@ export class OpCkeditorFormComponent implements OnInit {
this.wrappedTextArea = this.formElement.find(this.textareaSelector);
this.wrappedTextArea.hide();
const wrapper = this.$element.find(`.${ckEditorReplacementClass}`);
const context = { resource: this.resource,
previewContext: this.previewContext };
const editorPromise = this.ckEditorSetup
.create('classic', wrapper[0], { resource: this.resource })
.create('classic',
wrapper[0],
context)
.then(this.setup.bind(this));
this.$element.data('editor', editorPromise);

@ -61,7 +61,8 @@ export class EditorMacrosService {
*/
public configureWikiPageInclude(page:string):Promise<string> {
return new Promise<string>((resolve, _) => {
const modal = this.opModalService.show(WikiIncludePageMacroModal, { page: page });
const pageValue = page || '';
const modal = this.opModalService.show(WikiIncludePageMacroModal, { page: pageValue });
modal.closingEvent.subscribe((modal:WikiIncludePageMacroModal) => {
if (modal.changed) {
resolve(modal.page);

@ -92,7 +92,7 @@ export class ApiV3Paths {
let base = this.apiV3Base + '/render/markdown';
if (context) {
return base + `?link=${context}`;
return base + `?context=${context}`;
} else {
return base;
}

@ -70,8 +70,13 @@ export class FormattableEditField extends EditField {
public setupMarkdownEditor(container:HTMLElement) {
const element = container.querySelector('.op-ckeditor-element') as HTMLElement;
const context = { resource: this.resource,
previewContext: this.previewContext };
this.ckEditorSetup
.create('balloon', element, { resource: this.resource })
.create('balloon',
element,
context)
.then((editor:ICKEditorInstance) => {
this.ckeditor = editor;
if (this.rawValue) {
@ -89,6 +94,14 @@ export class FormattableEditField extends EditField {
} );
}
private get previewContext() {
if (this.resource.isNew && this.resource.project) {
return this.resource.project.href;
} else if (!this.resource.isNew) {
return this.pathHelper.api.v3.work_packages.id(this.resource.id).path;
}
}
public reset() {
this.ckeditor.setData(this.rawValue);
}

@ -36,8 +36,8 @@ module API
resources :render do
helpers do
SUPPORTED_CONTEXT_NAMESPACES = %w(work_packages projects news posts).freeze
SUPPORTED_MEDIA_TYPE = 'text/plain'
SUPPORTED_CONTEXT_NAMESPACES = %w(work_packages projects news posts wiki_pages).freeze
SUPPORTED_MEDIA_TYPE = 'text/plain'.freeze
def allowed_content_types
[SUPPORTED_MEDIA_TYPE]
@ -46,7 +46,7 @@ module API
def check_content_type
actual = request.content_type
unless actual && actual.starts_with?(SUPPORTED_MEDIA_TYPE)
unless actual&.starts_with?(SUPPORTED_MEDIA_TYPE)
bad_type = actual || I18n.t('api_v3.errors.missing_content_type')
message = I18n.t('api_v3.errors.invalid_content_type',
content_type: SUPPORTED_MEDIA_TYPE,
@ -82,14 +82,12 @@ module API
def try_context_object
if params[:context]
context = parse_context
namespace = context[:namespace]
klass = case context[:namespace]
when 'work_packages'
WorkPackage
when 'news'
News
when 'posts'
klass = if namespace == 'posts'
Message
elsif SUPPORTED_CONTEXT_NAMESPACES.without('posts').include?(namespace)
namespace.camelcase.singularize.constantize
end
return unless klass

@ -58,7 +58,7 @@ module OpenProject::TextFormatting::Formats
view_context.content_tag 'op-ckeditor-form',
'',
'textarea-selector': "##{field_id}",
'preview-context': context[:preview],
'preview-context': context[:preview_context],
'data-resource': resource.to_json
end
end

@ -59,8 +59,7 @@ class TabularFormBuilder < ActionView::Helpers::FormBuilder
if options[:with_text_formatting]
# use either the provided id or fetch the one created by rails
id = options[:id] || input.match(/<[^>]* id="(\w+)"[^>]*>/)[1]
context_object = object.persisted? ? object : nil
options[:preview_context] ||= preview_context(context_object)
options[:preview_context] ||= preview_context(object)
input.concat text_formatting_wrapper id, options
end

@ -94,6 +94,10 @@ describe 'Wysiwyg child pages spec',
# Find widget, click to show toolbar
placeholder = find('.macro.-child_pages')
# Placeholder states `this page` and no `Include parent`
expect(placeholder).to have_text('this page')
expect(placeholder).not_to have_text('Include parent')
# Edit widget and cancel again
placeholder.click
page.find('.ck-balloon-panel .ck-button', visible: :all, text: 'Edit').click
@ -110,6 +114,9 @@ describe 'Wysiwyg child pages spec',
# Save widget
find('.op-modal--submit-button').click
# Placeholder states `parent-page` and no `Include parent`
expect(placeholder).to have_text('parent-page')
expect(placeholder).not_to have_text('Include parent')
end
# Save wiki page
@ -126,7 +133,7 @@ describe 'Wysiwyg child pages spec',
find('.toolbar .icon-edit').click
end
editor.in_editor do |container, editable|
editor.in_editor do |_container, _editable|
# Find widget, click to show toolbar
placeholder = find('.macro.-child_pages')
@ -138,6 +145,10 @@ describe 'Wysiwyg child pages spec',
# Save widget
find('.op-modal--submit-button').click
# Placeholder states `parent-page` and `Include parent`
expect(placeholder).to have_text('parent-page')
expect(placeholder).to have_text('Include parent')
end
# Save wiki page

@ -96,10 +96,13 @@ describe 'Wysiwyg include wiki page spec',
find('.op-modal--submit-button').click
# Find widget, click to show toolbar
modal = find('.macro.-wiki_page_include')
placeholder = find('.macro.-wiki_page_include')
# Placeholder states `included`
expect(placeholder).to have_text('included')
# Edit widget again
modal.click
placeholder.click
page.find('.ck-balloon-panel .ck-button', visible: :all, text: 'Edit').click
expect(page).to have_field('selected-page', with: 'included')
find('.op-modal--cancel-button').click

Loading…
Cancel
Save