Index: openacs-4/packages/richtext-ckeditor4/richtext-ckeditor4.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor4/richtext-ckeditor4.info,v diff -u -N -r1.2 -r1.3 --- openacs-4/packages/richtext-ckeditor4/richtext-ckeditor4.info 7 Aug 2017 23:48:14 -0000 1.2 +++ openacs-4/packages/richtext-ckeditor4/richtext-ckeditor4.info 16 Aug 2017 13:10:20 -0000 1.3 @@ -9,14 +9,14 @@ f f - + Gustaf Neumann Richtext editor plugin for integrating CKeditor 4 with acs-templating 2017-08-06 2 - + Index: openacs-4/packages/richtext-ckeditor4/tcl/ckfinder-init.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor4/tcl/ckfinder-init.tcl,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/richtext-ckeditor4/tcl/ckfinder-init.tcl 16 Aug 2017 13:10:20 -0000 1.1 @@ -0,0 +1,142 @@ +# +# 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. +# +# 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 ckeditor4 +# plugins "uploadimage" +# }} +# +# For attaching the images, make sure to pass the property +# "displayed_object_id" on the page, where the richtext form is +# displayed. +# + +# +# Upload handler +# +ns_register_proc POST $::richtext::ckeditor4::ckfinder_url/upload { + # + # 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. + # + set complaints [::richtext::ckeditor4::ckfinder::query_page_contract { + {object_id:naturalnum} + {type:word} + }] + + if {[llength $complaints] == 0 && $type eq "Images"} { + + set form [ns_getform] + set d [::richtext::ckeditor4::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] \ + ] + set success [dict get $d success] + if {$success eq "1"} { + # + # Successful operation + # + set view_url [export_vars \ + -base $::richtext::ckeditor4::ckfinder_url/view { + {image_id "[dict get $d image_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 + # + set errMsg "invalid query parameter for 'type' // $complaints" + } + + if {$success eq "0"} { + set reply [subst {{ + "uploaded": [dict get $d success], + "error": { + "message": "[dict get $d errMsg]", + } + }}] + } + ns_log notice $reply + + ns_return 200 text/plain $reply +} + +# +# View handler +# + +ns_register_proc GET $::richtext::ckeditor4::ckfinder_url/view { + set ::template::parse_level [info level] + ad_try { + # + # Use the standard page_contract + # + ad_page_contract { + } { + {image_id:naturalnum ""} + } + ::richtext::ckeditor4::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-ckeditor4/tcl/richtext-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/richtext-ckeditor4/tcl/richtext-procs.tcl,v diff -u -N -r1.2 -r1.3 --- openacs-4/packages/richtext-ckeditor4/tcl/richtext-procs.tcl 7 Aug 2017 23:48:14 -0000 1.2 +++ openacs-4/packages/richtext-ckeditor4/tcl/richtext-procs.tcl 16 Aug 2017 13:10:20 -0000 1.3 @@ -5,35 +5,66 @@ This script defines the following two procs: ::richtext-ckeditor4::initialize_widget - ::richtext-ckeditor4::render_widgets - + ::richtext-ckeditor4::render_widgets + @author Gustaf Neumann @creation-date 1 Jan 2016 @cvs-id $Id$ } namespace eval ::richtext::ckeditor4 { - set version 4.7.1 - set ck_package standard - + set package_id [apm_package_id_from_key "richtext-ckeditor4"] + + # ns_section ns/server/${server}/acs/richtext-ckeditor + # ns_param CKEditorVersion 4.7.1 + # 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 4.7.1] + 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 ""] + + # + # The cp_package might be basic, standard, of full; + # + # Use "custom" for customized downloads, expand the downloaded zip file in + # richtext-ckeditor4/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 "standard"] + ad_proc initialize_widget { -form_id -text_id {-options {}} } { - + Initialize an CKEditor richtext editor widget. - + } { ns_log debug "initialize CKEditor instance with <$options>" - # allow per default all classes, unless the user has specified + # 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 # ckeditor4}" or via the package parameter "RichTextEditor" of # acs-templating. @@ -44,37 +75,58 @@ # plugins skin customConfig spellcheck # set ckOptionsList {} - + if {![dict exists $options spellcheck]} { + set package_id [apm_package_id_from_key "richtext-ckeditor4"] dict set options spellcheck [parameter::get \ - -package_id [apm_package_id_from_key "richtext-ckeditor4"] \ + -package_id $package_id \ -parameter "SCAYT" \ -default "false"] } - # For the native spellchecker, one has to hold "ctrl" or "cmd" # with the right click. - + + # + # 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 + } + } + set upload_url [export_vars \ + -base $::richtext::ckeditor4::ckfinder_url/upload { + {object_id $displayed_object_id} {type Images} + }] lappend ckOptionsList \ "language: '[lang::conn::language]'" \ "disableNativeSpellChecker: false" \ - "scayt_autoStartup: [dict get $options spellcheck]" + "scayt_autoStartup: [dict get $options spellcheck]" \ + "imageUploadUrl: '$upload_url'" + set plugins [split $::richtext::ckeditor4::standard_plugins ,] if {[dict exists $options plugins]} { - lappend ckOptionsList "extraPlugins: '[dict get $options plugins]'" + lappend plugins {*}[split [dict get $options plugins] ,] } + if {[llength $plugins] > 0} { + lappend ckOptionsList "extraPlugins: '[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]'" + lappend ckOptionsList \ + "customConfig: '[dict get $options customConfig]'" } if {[dict exists $options extraAllowedContent]} { - lappend ckOptionsList "extraAllowedContent: '[dict get $options extraAllowedContent]'" + lappend ckOptionsList \ + "extraAllowedContent: '[dict get $options extraAllowedContent]'" } set ckOptions [join $ckOptionsList ", "] - + # # Add the configuration via body script # @@ -86,7 +138,7 @@ # Load the editor and everything necessary to the current page. # ::richtext::ckeditor4::add_editor - + # # do we need render_widgets? # @@ -102,7 +154,7 @@ ::acs_blank_master(ckeditor4) ::acs_blank_master__htmlareas - + } { # # In case no ckeditor4 instances are created, nothing has to be @@ -125,7 +177,7 @@ 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 @@ -140,7 +192,8 @@ set suffix $version/$ck_package/ckeditor.js set resources $::acs::rootdir/packages/richtext-ckeditor4/www/resources - + ns_log notice "ACS::ROOT <$::acs::rootdir>" + ns_log notice "CHECK <$resources/$suffix>" if {[file exists $resources/$suffix]} { lappend result file $resources/$suffix lappend result resources /resources/richtext-ckeditor4/$suffix @@ -149,7 +202,7 @@ return $result } - + ad_proc ::richtext::ckeditor4::add_editor { {-ck_package ""} {-version ""} @@ -164,14 +217,15 @@ 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::ckeditor4::version_info \ -ck_package $ck_package \ -version $version] if {[dict exists $version_info resources]} { - template::head::add_javascript -src [dict get $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 @@ -184,13 +238,16 @@ # 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::ckeditor4::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 @@ -199,7 +256,7 @@ 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 @@ -220,9 +277,9 @@ # set unzip [::util::which unzip] if {$unzip eq ""} { - error "can't install CKeditor locally; no unzip program found on PATH" + error "can't install CKeditor locally; no unzip program found on PATH" } - + # # Do we have a writable output directory under resources? # @@ -245,14 +302,17 @@ # set fn [dict get $result file] set output [exec $unzip -o $fn -d $resources/$version] - file rename -- $resources/$version/ckeditor $resources/$version/$ck_package + file rename -- \ + $resources/$version/ckeditor \ + $resources/$version/$ck_package } else { error "download of $download_url failed, HTTP status: [dict get $result status]" } } } + # Local variables: # mode: tcl # tcl-indent-level: 4