Index: openacs-4/packages/richtext-ckeditor5/richtext-ckeditor5.info
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/richtext-ckeditor5.info,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/richtext-ckeditor5.info 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1,33 @@
+
+
+
+
+ Richtext CKeditor5
+ Richtext CKeditor5
+ f
+ t
+ f
+ f
+
+
+ Gustaf Neumann
+ Richtext editor plugin for integrating CKeditor 5
+ with the acs-templating richtext widget
+ 2017-12-05
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: openacs-4/packages/richtext-ckeditor5/lib/file-browser.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/lib/file-browser.adp,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/lib/file-browser.adp 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1,88 @@
+
+
+
+
+ @title@
+
+
+
+
+
+
@page_title@
+
+
+
+
+
+
+
+
+
+
@no_attachment@
+
+
+
+
+
+
+
+
Index: openacs-4/packages/richtext-ckeditor5/lib/file-browser.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/lib/file-browser.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/lib/file-browser.tcl 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1,27 @@
+ad_include_contract {
+} {
+ {object_id:naturalnum}
+ {type:word}
+ {CKEditorFuncNum ""}
+ {CKEditor:word ""}
+ {langCode en}
+}
+
+set title "Browse Attached Images"
+set page_title "Selectable Images"
+set no_attachment "No Attachment Available"
+
+template::multirow create images src alt
+
+foreach tuple [attachments::get_attachments -object_id $object_id] {
+ lassign $tuple image_id .
+ template::multirow append images \
+ /acs-content-repository/ckfinder/view?image_id=$image_id ""
+}
+
+#
+# Local variables:
+# mode: tcl
+# tcl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
Index: openacs-4/packages/richtext-ckeditor5/tcl/ckfinder-init.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/tcl/ckfinder-init.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/tcl/ckfinder-init.tcl 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1,246 @@
+#
+# This is a minimal AJAX based ckfinder interface.
+#
+# It supports currently just the drag and drop interface of the
+# "uploadimage" plugin. Dropped images are uploaded to the content
+# repository and attached to the displayed object_id via the
+# attachment package.
+#
+# Since it is not clear, what is the best place for mounting the
+# package (the richtext-* is a singleton package, what should be done
+# e.g. on host-node mapped subsites? Should we add some support to
+# acs-subsite or acs-content-repository), we just register the few
+# URLs .../upload and .../view via ns_register_proc. This might change
+# in the future.
+#
+# NOTE: the delivery of files performs two permission checks, once in
+# the request processor (checking the site nodes) and once for the
+# concrete image. In order to make uploaded images readable by "The
+# Public", make sure that the package_id pointed to by the CKFinderURL
+# (per default /acs-content-repository) offers as well read
+# permissions to the public.
+
+# This interface can be used obtaining a customized version of
+# CKEditor containing he "uploadimage" plugin. When this is installed,
+# it can be used e.g. with a widget spec like the following
+#
+# {text:richtext(richtext)
+# {html {...}}
+# {label "...."}
+# {options {
+# editor ckeditor5
+# plugins "uploadimage"
+# }}
+#
+# For attaching the images, make sure to pass the property
+# "displayed_object_id" on the page, where the richtext form is
+# displayed.
+#
+
+#
+# We need here a small helper for input checking using the usual
+# checkers for two reasons:
+#
+# 1) The way ckfinder is recommended to work relies on the
+# separate processing of QUERY and POST variables of an
+# request. The traditional OpenACS input handling does NOT
+# support both types of variables at the same time. so we use
+# here a small helper, such we can use at least the
+# traditional calling conventions and page contract filters.
+#
+# 2) The classical page_contract cannot be configured to interact
+# properly with AJAX, at least not with a predefined AJAX
+# interface expecting always a certain JSON array as result.
+# This corresponds to "responseType=json" in the uploadUrl.
+#
+
+ns_register_proc POST $::richtext::ckeditor5::ckfinder_url/uploadimage {
+ #
+ # Image upload handler (for "uploadimage" plugin)
+ #
+ set complaints [::richtext::ckeditor5::ckfinder::query_page_contract {
+ {object_id:naturalnum}
+ {type:word}
+ }]
+
+ if {[llength $complaints] == 0 && $type eq "Images"} {
+
+ set form [ns_getform]
+ set d [::richtext::ckeditor5::ckfinder::image_attach \
+ -object_id $object_id \
+ -import_file [ns_set get $form upload.tmpfile] \
+ -mime_type [ns_set get $form upload.content-type] \
+ -user_id [ad_conn user_id] \
+ -peeraddr [ad_conn peeraddr] \
+ -package_id [ad_conn package_id] \
+ -image \
+ ]
+ set success [dict get $d success]
+ if {$success eq "1"} {
+ #
+ # Successful operation
+ #
+ set view_url [export_vars \
+ -base $::richtext::ckeditor5::ckfinder_url/view {
+ {image_id "[dict get $d file_id]"}
+ }]
+ set reply [subst {{
+ "uploaded": [dict get $d success],
+ "fileName": "[dict get $d name]",
+ "url": "$view_url",
+ "width": [dict get $d width],
+ "height": [dict get $d height]
+ }}]
+ } else {
+ #
+ # ckfinder::image_attach returned an error
+ #
+ set errMsg [dict get $d errMsg]
+ }
+ } else {
+ #
+ # Either page contract failed or invalid value for 'type' was
+ # specified
+ #
+ dict set d errMsg "invalid query parameter // $complaints"
+ set success 0
+ }
+
+ if {$success eq "0"} {
+ set reply [subst {{
+ "uploaded": $success,
+ "error": {
+ "message": "[dict get $d errMsg]",
+ }
+ }}]
+ }
+ ns_log notice $reply
+
+ ns_return 200 text/plain $reply
+}
+
+
+ns_register_proc POST $::richtext::ckeditor5::ckfinder_url/upload {
+ #
+ # Upload handler (for the standard "filebrowser" plugin)
+ #
+ set complaints [::richtext::ckeditor5::ckfinder::query_page_contract {
+ {object_id:naturalnum}
+ {type:word}
+ {CKEditorFuncNum ""}
+ {command:word ""}
+ {CKEditor:word ""}
+ {langCode en}
+ }]
+
+ if {[llength $complaints] == 0 && $type eq "Files"} {
+
+ set form [ns_getform]
+ set d [::richtext::ckeditor5::ckfinder::file_attach \
+ -object_id $object_id \
+ -import_file [ns_set get $form upload.tmpfile] \
+ -mime_type [ns_set get $form upload.content-type] \
+ -user_id [ad_conn user_id] \
+ -peeraddr [ad_conn peeraddr] \
+ -package_id [ad_conn package_id] \
+ ]
+ set success [dict get $d success]
+ if {$success eq "1"} {
+ #
+ # Successful operation
+ #
+ set view_url [export_vars \
+ -base $::richtext::ckeditor5::ckfinder_url/view {
+ {image_id "[dict get $d file_id]"}
+ }]
+ set reply [subst {
+
+ }]
+ } else {
+ #
+ # ckfinder::image_attach returned an error
+ #
+ set errMsg [dict get $d errMsg]
+ }
+ } else {
+ #
+ # Either page contract failed or invalid value for 'type' was
+ # specified
+ #
+ dict set d errMsg "invalid query parameter // $complaints"
+ set success 0
+ }
+
+ if {$success eq "0"} {
+ set reply [subst {[dict get $d errMsg]}]
+ }
+ ns_log notice $reply
+ ns_return 200 text/html $reply
+}
+
+ns_register_proc GET $::richtext::ckeditor5::ckfinder_url/browse {
+ #
+ # File-browser (for the standard "filebrowser" plugin)
+ #
+ set complaints [::richtext::ckeditor5::ckfinder::query_page_contract {
+ {object_id:naturalnum}
+ {type:word}
+ {CKEditorFuncNum ""}
+ {CKEditor:word ""}
+ {langCode en}
+ }]
+
+ permission::require_permission \
+ -party_id [ad_conn user_id] \
+ -object_id $object_id \
+ -privilege read
+
+ set reply [template::adp_include \
+ /packages/richtext-ckeditor5/lib/file-browser [subst {
+ object_id "$object_id"
+ type "$type"
+ CKEditorFuncNum "$CKEditorFuncNum"
+ CKEditor "$CKEditor"
+ langCode "$langCode"
+ }]]
+
+ ns_return 200 text/html $reply
+}
+
+#
+# View handler
+#
+
+ns_register_proc GET $::richtext::ckeditor5::ckfinder_url/view {
+ #
+ # View function (for "filebrowser" and "uploadimage" plugins)
+ #
+ set ::template::parse_level [info level]
+ ad_try {
+ #
+ # Use the standard page_contract
+ #
+ ad_page_contract {
+ } {
+ {image_id:naturalnum ""}
+ }
+ ::richtext::ckeditor5::ckfinder::return_file \
+ -revision_id $image_id \
+ -user_id [ad_conn user_id]
+
+ } ad_script_abort val {
+ #
+ # The page contract has probably failed, no need to raise an
+ # exception.
+ #
+ }
+}
+
+#
+# Local variables:
+# mode: tcl
+# tcl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
Index: openacs-4/packages/richtext-ckeditor5/tcl/ckfinder-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/tcl/ckfinder-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/tcl/ckfinder-procs.tcl 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1,224 @@
+ad_library {
+
+ CKEditor 5 helper for ckfinder interface (not complete)
+
+ This script defines the following public procs:
+
+ ::richtext::ckeditor5::ckfinder::image_attach
+ ::richtext::ckeditor5::ckfinder::return_file
+
+ @author Gustaf Neumann
+ @creation-date 15 Aug 2017
+ @cvs-id $Id: ckfinder-procs.tcl,v 1.1 2017/12/05 15:51:26 gustafn Exp $
+
+ NOTE: the delivery of files performs two permission checks, once
+ in the requestprocessor (the site nodes) and once for the concrete image.
+ If one whishes to make uploaded images readable by "The Public", make
+ sure that the /
+
+}
+
+namespace eval ::richtext::ckeditor5::ckfinder {
+
+ ad_proc -public file_attach {
+ -import_file
+ -mime_type
+ -object_id
+ {-privilege read}
+ -user_id
+ -peeraddr
+ -package_id
+ {-image:boolean}
+ } {
+
+ Insert the provided image file to the content repository as a
+ new item and attach the image to the specified object_id via
+ the attachment API. This makes sure that the image will be
+ deleted from the content repository, when the provided
+ object_id is deleted.
+
+ The user must have at least "read" privileges on the object,
+ but other stronger privileges can be supplied via parameter.
+
+ } {
+ permission::require_permission \
+ -party_id $user_id \
+ -object_id $object_id \
+ -privilege $privilege
+
+ if {$image_p} {
+ #
+ # Check if we can handle the mime type. Currently, only the
+ # following four mime types are supported, since these are
+ # supported by "ns_imgsize", which is used to determine the
+ # dimensions of the image.
+ #
+ switch -- $mime_type {
+ image/jpg -
+ image/jpeg -
+ image/gif -
+ image/png {
+ set ext .[lindex [split $mime_type /] 1]
+ lassign [ns_imgsize $import_file] width height
+ set success 1
+ }
+ default {
+ ns_log warning "image_attach: can't handle image type '$mime_type'"
+ return [list \
+ success 0 \
+ errMsg "can't handle image type '$mime_type'"]
+ }
+ }
+ } else {
+ set width 0
+ set height 0
+ set success 1
+ }
+ #
+ # Create a new item without child_rels
+ #
+ set name $object_id-[clock clicks -microseconds]
+ set item_id [::xo::db::sql::content_item new \
+ -name $name \
+ -parent_id [require_root_folder] \
+ -context_id $object_id \
+ -creation_user $user_id \
+ -creation_ip $peeraddr \
+ -item_subtype "content_item" \
+ -storage_type "file" \
+ -package_id $package_id \
+ -with_child_rels f]
+
+ #
+ # Create a revision for the fresh content_item
+ #
+ set revision_id [xo::dc nextval acs_object_id_seq]
+ content::revision::new \
+ -revision_id $revision_id \
+ -item_id $item_id \
+ -title $name \
+ -is_live t \
+ -creation_user $user_id \
+ -creation_ip $peeraddr \
+ -content_type "content_revision" \
+ -package_id $package_id \
+ -tmp_filename $import_file \
+ -mime_type $mime_type
+
+ #
+ # Attach the image to the object via the attachments API
+ #
+ attachments::attach \
+ -object_id $object_id \
+ -attachment_id $revision_id
+
+ return [list \
+ success $success \
+ name $name \
+ file_id $revision_id \
+ width $width \
+ height $height \
+ ]
+ }
+
+ ad_proc -public return_file {
+ -revision_id
+ -user_id
+ } {
+
+ Return the file with the specified revision_id to the
+ user. The user must have at read permissions to obtain the
+ file (image).
+
+ } {
+ permission::require_permission \
+ -party_id $user_id \
+ -object_id $revision_id \
+ -privilege read
+
+ set file_path [content::revision::get_cr_file_path \
+ -revision_id $revision_id]
+ set mime_type [db_string get_mime_type {
+ select mime_type from cr_revisions where revision_id = :revision_id
+ }]
+ ad_returnfile_background 200 $mime_type $file_path
+ }
+
+ ad_proc -private require_root_folder {
+ {-parent_id -100}
+ {-name attachments}
+ } {
+
+ Helper function to find the root folder for ckfinder
+ attachments.
+
+ } {
+ set root_folder_id [content::item::get_id \
+ -root_folder_id $parent_id \
+ -item_path $name]
+ if {$root_folder_id eq ""} {
+ set root_folder_id [content::item::new \
+ -name $name \
+ -parent_id $parent_id]
+ }
+ return $root_folder_id
+ }
+
+
+ ad_proc -private query_page_contract {
+ {-level 1}
+ params
+ } {
+
+ Helper function similar to ad_page_contract, but works only on
+ query variables.
+
+ @result return a list of complaints, which is empty in case of success
+
+ } {
+ #
+ # Process params provided by the query
+ #
+ foreach p [split [ns_conn query] &] {
+ lassign [split $p =] var value
+ set param($var) $value
+ }
+ #ns_log notice "provided params [array get param]"
+ #
+ # Process params as specified in the page contract
+ #
+ foreach p $params {
+ lassign $p spec default
+ lassign [split $spec :] name filters
+ #ns_log notice "param $name exists [info exists param($name)]"
+ if {[info exists param($name)]} {
+ set value $param($name)
+ #
+ # Call every page contract filter for this
+ # parameter. On failures, complaints are added to a
+ # global variable which is picked-up later.
+ #
+ foreach filter [split $filters ,] {
+ ad_page_contract_filter_invoke $filter $name value
+ }
+ } else {
+ set param($name) $default
+ }
+ uplevel $level [list set $name $param($name)]
+ }
+ if {[info exists ::ad_page_contract_complaints]} {
+ set complaints [ad_complaints_get_list]
+ } else {
+ set complaints ""
+ }
+ return $complaints
+ }
+
+}
+
+#
+# Local variables:
+# mode: tcl
+# tcl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
Index: openacs-4/packages/richtext-ckeditor5/tcl/richtext-init.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/tcl/richtext-init.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/tcl/richtext-init.tcl 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1 @@
+template::util::richtext::register_editor ckeditor5
Index: openacs-4/packages/richtext-ckeditor5/tcl/richtext-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/tcl/richtext-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/tcl/richtext-procs.tcl 5 Dec 2017 15:51:26 -0000 1.1
@@ -0,0 +1,389 @@
+ad_library {
+
+ CKEditor 5 integration with the richtext widget of acs-templating.
+
+ In addition to the richttext widget properties,
+ https://openacs.org/api-doc/proc-view?proc=template::widget::richtext&source_p=1
+ the CKEditor 5 allows us to specify a editor class
+
+ Note: CKEditor 5 no longer comes with a configuration setting to change its height.
+ https://stackoverflow.com/questions/46559354/how-to-set-the-height-of-ckeditor-5-classic-editor/46559355#46559355
+
+ The current release 1.0.0-alpha.2 not for easy imaging support "Coming soon"
+
+ This script defines in essence following two procs:
+
+ ::richtext-ckeditor5::initialize_widget
+ ::richtext-ckeditor5::render_widgets
+
+ @author Gustaf Neumann
+ @creation-date 2 Dec 2017
+ @cvs-id $Id: richtext-procs.tcl,v 1.1 2017/12/05 15:51:26 gustafn Exp $
+}
+
+namespace eval ::richtext::ckeditor5 {
+
+ set package_id [apm_package_id_from_key "richtext-ckeditor5"]
+
+ # ns_section ns/server/${server}/acs/richtext-ckeditor
+ # ns_param CKEditorVersion 1.0.0-alpha.2
+ # ns_param CKEditorPackage standard
+ # ns_param CKFinderURL /acs-content-repository/ckfinder
+ # ns_param StandardPlugins uploadimage
+ #
+ set version [parameter::get \
+ -package_id $package_id \
+ -parameter CKEditorVersion \
+ -default 1.0.0-alpha.2]
+ set ckfinder_url [parameter::get \
+ -package_id $package_id \
+ -parameter CKFinderURL \
+ -default /acs-content-repository/ckfinder]
+ set standard_plugins [parameter::get \
+ -package_id $package_id \
+ -parameter StandardPlugins \
+ -default ""]
+ set JSEditorClass [parameter::get \
+ -package_id $package_id \
+ -parameter JSEditorClass \
+ -default ClassicEditor]
+ # ClassicEditor | BalloonEditor | InlineEditor
+
+ #
+ # The cp_package might be basic, standard, of full;
+ #
+ # Use "custom" for customized downloads, expand the downloaded zip file in
+ # richtext-ckeditor5/www/resources/$version
+ # and rename the expanded top-folder from "ckeditor" to "custom"
+ #
+ set ck_package [parameter::get \
+ -package_id $package_id \
+ -parameter CKEditorPackage \
+ -default "classic"]
+
+ ad_proc initialize_widget {
+ -form_id
+ -text_id
+ {-options {}}
+ } {
+
+ Initialize an CKEditor richtext editor widget.
+
+ } {
+ ns_log notice "initialize CKEditor instance with <$options>"
+
+ # Allow per default all CSS-classes, unless the user has specified
+ # it differently
+ if {![dict exists $options extraAllowedContent]} {
+ dict set options extraAllowedContent {*(*)}
+ }
+
+ #
+ # The richtext widget might be specified by "options {editor
+ # ckeditor5}" or via the package parameter "RichTextEditor" of
+ # acs-templating.
+ #
+ # The following options handled by the CKEditor integration
+ # can be specified in the widget spec of the richtext widget:
+ #
+ # plugins skin customConfig spellcheck
+ #
+ set ckOptionsList {}
+
+ if {![dict exists $options spellcheck]} {
+ set package_id [apm_package_id_from_key "richtext-ckeditor5"]
+ dict set options spellcheck [parameter::get \
+ -package_id $package_id \
+ -parameter "SCAYT" \
+ -default "false"]
+ }
+ # For the native spellchecker, one has to hold "ctrl" or "cmd"
+ # with the right click.
+
+ lappend ckOptionsList \
+ "language: '[lang::conn::language]'" \
+ "disableNativeSpellChecker: false" \
+ "scayt_autoStartup: [dict get $options spellcheck]"
+
+ #
+ # Get the property "displayed_object_id" from the call-stack
+ #
+ for {set l 0} {$l < [info level]} {incr l} {
+ set propVar __adp_properties(displayed_object_id)
+ if {[uplevel #$l [list info exists $propVar]]} {
+ set displayed_object_id [uplevel #$l [list set $propVar]]
+ break
+ }
+ }
+ if {[info exists displayed_object_id]} {
+ #
+ # If we have a displayed_object_id, configure it for the
+ # plugins "filebrowser" and "uploadimage".
+ #
+ set image_upload_url [export_vars \
+ -base $::richtext::ckeditor5::ckfinder_url/uploadimage {
+ {object_id $displayed_object_id} {type Images}
+ }]
+ set file_upload_url [export_vars \
+ -base $::richtext::ckeditor5::ckfinder_url/upload {
+ {object_id $displayed_object_id} {type Files} {command QuickUpload}
+ }]
+ set file_browse_url [export_vars \
+ -base $::richtext::ckeditor5::ckfinder_url/browse {
+ {object_id $displayed_object_id} {type Files}
+ }]
+ lappend ckOptionsList \
+ "imageUploadUrl: '$image_upload_url'" \
+ "filebrowserBrowseUrl: '$file_browse_url'" \
+ "filebrowserUploadUrl: '$file_upload_url'" \
+ "filebrowserWindowWidth: '800'" \
+ "filebrowserWindowHeight: '600'"
+ }
+
+ set plugins [split $::richtext::ckeditor5::standard_plugins ,]
+ if {[dict exists $options plugins]} {
+ lappend plugins {*}[split [dict get $options plugins] ,]
+ }
+ if {[llength $plugins] > 0} {
+ lappend ckOptionsList "plugins: \[ [join $plugins ,] \]"
+ }
+ if {[dict exists $options skin]} {
+ lappend ckOptionsList "skin: '[dict get $options skin]'"
+ }
+ if {[dict exists $options customConfig]} {
+ lappend ckOptionsList \
+ "customConfig: '[dict get $options customConfig]'"
+ }
+ if {[dict exists $options extraAllowedContent]} {
+ lappend ckOptionsList \
+ "extraAllowedContent: '[dict get $options extraAllowedContent]'"
+ }
+ #
+ # For the time being, set the global variable
+ # ::richtext::ckeditor5::JSEditorClass of the JavaScript
+ # editor class to the provided value, since we need this value
+ # for computing the richt CDN url.
+ #
+ if {[dict exists $options JSEditorClass]} {
+ set ::richtext::ckeditor5::JSEditorClass [dict get $options JSEditorClass]
+ }
+ set JSEditorClass $::richtext::ckeditor5::JSEditorClass
+
+ set ckOptions [join $ckOptionsList ", "]
+
+ #
+ # Add the configuration via body script
+ #
+ ns_log notice "initialize_widget: $JSEditorClass.create(document.querySelector( '#$text_id', {$ckOptions} )"
+ template::add_script -section body -script [subst {
+ $JSEditorClass
+ .create( document.querySelector( '#$text_id', {$ckOptions} ))
+ .catch( error => {
+ console.error( error );
+ } );
+ }]
+
+ #
+ # Load the editor and everything necessary to the current page.
+ #
+ ::richtext::ckeditor5::add_editor
+
+ #
+ # do we need render_widgets?
+ #
+ return ""
+ }
+
+
+ ad_proc render_widgets {} {
+
+ Render the ckeditor5 rich-text widgets. This function is created
+ at a time when all rich-text widgets of this page are already
+ initialized. The function is controlled via the global variables
+
+ ::acs_blank_master(ckeditor5)
+ ::acs_blank_master__htmlareas
+
+ } {
+ #
+ # In case no ckeditor5 instances are created, nothing has to be
+ # done.
+ #
+ if {![info exists ::acs_blank_master(ckeditor5)]} {
+ return
+ }
+ #
+ # Since "template::head::add_javascript -src ..." prevents
+ # loading the same resource multiple times, we can perform the
+ # load in the per-widget initialization and we are done here.
+ #
+ }
+
+ ad_proc ::richtext::ckeditor5::version_info {
+ {-ck_package ""}
+ {-version ""}
+ } {
+
+ Get information about available version(s) of CKEditor, either
+ from the local file system, or from CDN.
+
+ } {
+ #
+ # If no version or ck editor package are specified, use the
+ # namespaced variables as default.
+ #
+ if {$version eq ""} {
+ set version ${::richtext::ckeditor5::version}
+ }
+ if {$ck_package eq ""} {
+ switch ${::richtext::ckeditor5::JSEditorClass} {
+ ClassicEditor { set ck_package classic}
+ BalloonEditor { set ck_package balloon}
+ InlineEditor { set ck_package inline}
+ default { set ck_package ${::richtext::ckeditor5::ck_package}}
+ }
+ }
+ ns_log notice "CKeditor setting ck_package to <${::richtext::ckeditor5::ck_package}> editorclass $::richtext::ckeditor5::JSEditorClass"
+ set ::richtext::ckeditor5::ck_package ${::richtext::ckeditor5::ck_package}
+
+ set suffix ckeditor5/$version/$ck_package/ckeditor.js
+ set resources $::acs::rootdir/packages/richtext-ckeditor5/www/resources
+ if {[file exists $resources/$suffix]} {
+ lappend result file $resources/$suffix
+ lappend result resources /resources/richtext-ckeditor5/$suffix
+ }
+ lappend result cdn "//cdn.ckeditor.com/$suffix"
+ ns_log notice "CKEditor path <$result> "
+ # https://cdn.ckeditor.com/ckeditor5/1.0.0-alpha.2/classic/ckeditor.js
+
+ return $result
+ }
+
+ ad_proc ::richtext::ckeditor5::add_editor {
+ {-ck_package ""}
+ {-version ""}
+ } {
+
+ Add the necessary JavaScript and other files to the current
+ page. The naming is modeled after "add_script", "add_css",
+ ... but is intended to care about everything necessary,
+ including the content security policies. Similar naming
+ conventions should be used for other editors as well.
+
+ This function can be as well used from other packages, such
+ e.g. from the xowiki form-fields, which provide a much higher
+ customization.
+
+ } {
+ set version_info [::richtext::ckeditor5::version_info \
+ -ck_package $ck_package \
+ -version $version]
+
+ if {[dict exists $version_info resources]} {
+ template::head::add_javascript \
+ -src [dict get $version_info resources]
+ } else {
+ template::head::add_javascript -src [dict get $version_info cdn]
+ security::csp::require script-src cdn.ckeditor.com
+ security::csp::require style-src cdn.ckeditor.com
+ security::csp::require img-src cdn.ckeditor.com
+ }
+
+ #
+ # add required general directives for content security policies
+ #
+ #security::csp::require script-src 'unsafe-eval'
+ security::csp::require -force script-src 'unsafe-inline'
+
+ # this is needed currently for "imageUploadUrl"
+ security::csp::require img-src data:
+ }
+
+ ad_proc ::richtext::ckeditor5::download {
+ {-ck_package ""}
+ {-version ""}
+ } {
+
+ Download the CKeditor package in the specified version and put
+ it into a directory structure similar to the CDN structure to
+ allow installation of multiple versions. When the local
+ structure is available, it will be used by initialize_widget.
+
+ Notice, that for this automated download, the "unzip" program
+ must be installed and $::acs::rootdir/packages/www must be
+ writable by the web server.
+
+ } {
+ #
+ # If no version or ck editor package are specified, use the
+ # namespaced variables as default.
+ #
+ if {$version eq ""} {
+ set version ${::richtext::ckeditor5::version}
+ }
+ if {$ck_package eq ""} {
+ set ck_package ${::richtext::ckeditor5::ck_package}
+ }
+
+ set download_url http://download.cksource.com/CKEditor/CKEditor/CKEditor%20${version}/ckeditor_${version}_${ck_package}.zip
+ set resources $::acs::rootdir/packages/richtext-ckeditor5/www/resources
+
+ #
+ # Do we have unzip installed?
+ #
+ set unzip [::util::which unzip]
+ if {$unzip eq ""} {
+ error "can't install CKeditor locally; no unzip program found on PATH"
+ }
+
+ #
+ # Do we have a writable output directory under resources?
+ #
+ if {![file isdirectory $resources/$version]} {
+ file mkdir $resources/$version
+ }
+ if {![file writable $resources/$version]} {
+ error "directory $resources/$version is not writable"
+ }
+
+ #
+ # So far, everything is fine, download the editor package
+ #
+ set result [util::http::get -url $download_url -spool]
+ #ns_log notice "GOT $result"
+ if {[dict get $result status] == 200} {
+ #
+ # The Download was successful, unzip it and let the
+ # directory structure look similar as on the CDN.
+ #
+ set fn [dict get $result file]
+ set output [exec $unzip -o $fn -d $resources/$version]
+ file rename -- \
+ $resources/$version/ckeditor \
+ $resources/$version/$ck_package
+ } else {
+ error "download of $download_url failed, HTTP status: [dict get $result status]"
+ }
+ }
+
+ ad_proc -public get_tag {-options} {
+ Return the tag for rendering
+ } {
+ ns_log notice "=== get_tag $options"
+ if {[dict exists $options editor]
+ && [dict get $options editor] eq "ckeditor5"
+ && [dict exists $options JSEditorClass]
+ && [dict get $options JSEditorClass] ne "ClassicEditor"
+ } {
+ set edit_item_tag div
+ }
+ }
+
+}
+
+
+# Local variables:
+# mode: tcl
+# tcl-indent-level: 4
+# indent-tabs-mode: nil
+# End:
Index: openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/download.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/download.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/download.tcl 5 Dec 2017 15:51:27 -0000 1.1
@@ -0,0 +1,11 @@
+ad_page_contract {
+ @author Gustaf Neumann
+
+ @creation-date Jan 04, 2017
+} {
+ {ck_package:word,notnull ""}
+ {version:word,notnull ""}
+}
+
+::richtext::ckeditor4::download -ck_package $ck_package -version $version
+ad_returnredirect .
Index: openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/index.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/index.adp,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/index.adp 5 Dec 2017 15:51:27 -0000 1.1
@@ -0,0 +1,20 @@
+
+@title;literal@
+@context;literal@
+
+
@title;noquote@
+
+The current version of CKEditor is @version@.
+
This version of CKEditor is installed locally
+under @resources@
+
In the current installation, this version of KEditor can be used via CDN @cdn@.
+
+
Do you want to download
+ version @version@ of CKEditor to your file system?
+
+
+
The directory @path@ is NOT writable for the server. In
+ order to be able to download the CKEditor via this web interface,
+ please change the permissions so that OpenACS can write to it.
+
+
.
\ No newline at end of file
Index: openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/index.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/richtext-ckeditor5/www/sitewide-admin/index.tcl 5 Dec 2017 15:51:27 -0000 1.1
@@ -0,0 +1,16 @@
+set title "Richtext CKEditor Sitewide Admin"
+set context [list $title]
+set version $::richtext::ckeditor4::version
+
+#
+# Get version info about CKEditor. If not locally installed, offer a
+# link for download.
+#
+set version_info [::richtext::ckeditor4::version_info]
+if {[dict exists $version_info resources]} {
+ set resources [dict get $version_info resources]
+}
+set cdn [dict get $version_info cdn]
+
+set path $::acs::rootdir/packages/richtext-ckeditor4/www
+set writable [file writable $path]