diff --git a/assets/3rdparty/bootstrap-wysiwyg.js b/assets/3rdparty/bootstrap-wysiwyg.js
new file mode 100644
index 00000000..75c52faa
--- /dev/null
+++ b/assets/3rdparty/bootstrap-wysiwyg.js
@@ -0,0 +1,333 @@
+/* jshint browser: true */
+
+( function( window, $ )
+{
+ "use strict";
+
+ /*
+ * Represenets an editor
+ * @constructor
+ * @param {DOMNode} element - The TEXTAREA element to add the Wysiwyg to.
+ * @param {object} userOptions - The default options selected by the user.
+ */
+ function Wysiwyg( element, userOptions ) {
+
+ // This calls the $ function, with the element as a parameter and
+ // returns the jQuery object wrapper for element. It also assigns the
+ // jQuery object wrapper to the property $editor on `this`.
+ this.selectedRange = null;
+ this.editor = $( element );
+ var editor = $( element );
+ var defaults = {
+ hotKeys: {
+ "Ctrl+b meta+b": "bold",
+ "Ctrl+i meta+i": "italic",
+ "Ctrl+u meta+u": "underline",
+ "Ctrl+z": "undo",
+ "Ctrl+y meta+y meta+shift+z": "redo",
+ "Ctrl+l meta+l": "justifyleft",
+ "Ctrl+r meta+r": "justifyright",
+ "Ctrl+e meta+e": "justifycenter",
+ "Ctrl+j meta+j": "justifyfull",
+ "Shift+tab": "outdent",
+ "tab": "indent"
+ },
+ toolbarSelector: "[data-role=editor-toolbar]",
+ commandRole: "edit",
+ activeToolbarClass: "btn-info",
+ selectionMarker: "edit-focus-marker",
+ selectionColor: "darkgrey",
+ dragAndDropImages: true,
+ keypressTimeout: 200,
+ fileUploadError: function( reason, detail ) { console.log( "File upload error", reason, detail ); }
+ };
+
+ var options = $.extend( true, {}, defaults, userOptions );
+ var toolbarBtnSelector = "a[data-" + options.commandRole + "],button[data-" + options.commandRole + "],input[type=button][data-" + options.commandRole + "]";
+ this.bindHotkeys( editor, options, toolbarBtnSelector );
+
+ if ( options.dragAndDropImages ) {
+ this.initFileDrops( editor, options, toolbarBtnSelector );
+ }
+
+ this.bindToolbar( editor, $( options.toolbarSelector ), options, toolbarBtnSelector );
+
+ editor.attr( "contenteditable", true )
+ .on( "mouseup keyup mouseout", function() {
+ this.saveSelection();
+ this.updateToolbar( editor, toolbarBtnSelector, options );
+ }.bind( this ) );
+
+ $( window ).bind( "touchend", function( e ) {
+ var isInside = ( editor.is( e.target ) || editor.has( e.target ).length > 0 ),
+ currentRange = this.getCurrentRange(),
+ clear = currentRange && ( currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset );
+
+ if ( !clear || isInside ) {
+ this.saveSelection();
+ this.updateToolbar( editor, toolbarBtnSelector, options );
+ }
+ } );
+ }
+
+ Wysiwyg.prototype.readFileIntoDataUrl = function( fileInfo ) {
+ var loader = $.Deferred(),
+ fReader = new FileReader();
+
+ fReader.onload = function( e ) {
+ loader.resolve( e.target.result );
+ };
+
+ fReader.onerror = loader.reject;
+ fReader.onprogress = loader.notify;
+ fReader.readAsDataURL( fileInfo );
+ return loader.promise();
+ };
+
+ Wysiwyg.prototype.cleanHtml = function( o ) {
+ var self = this;
+ if ( $( self ).data( "wysiwyg-html-mode" ) === true ) {
+ $( self ).html( $( self ).text() );
+ $( self ).attr( "contenteditable", true );
+ $( self ).data( "wysiwyg-html-mode", false );
+ }
+
+ // Strip the images with src="data:image/.." out;
+ if ( o === true && $( self ).parent().is( "form" ) ) {
+ var gGal = $( self ).html;
+ if ( $( gGal ).has( "img" ).length ) {
+ var gImages = $( "img", $( gGal ) );
+ var gResults = [];
+ var gEditor = $( self ).parent();
+ $.each( gImages, function( i, v ) {
+ if ( $( v ).attr( "src" ).match( /^data:image\/.*$/ ) ) {
+ gResults.push( gImages[ i ] );
+ $( gEditor ).prepend( "" );
+ $( v ).attr( "src", "postedimage/" + i );
+ }
+ } );
+ }
+ }
+
+ var html = $( self ).html();
+ return html && html.replace( /(
|\s|
<\/div>| )*$/, "" );
+ };
+
+ Wysiwyg.prototype.updateToolbar = function( editor, toolbarBtnSelector, options ) {
+ if ( options.activeToolbarClass ) {
+ $( options.toolbarSelector ).find( toolbarBtnSelector ).each( function() {
+ var self = $( this );
+ var commandArr = self.data( options.commandRole ).split( " " );
+ var command = commandArr[ 0 ];
+
+ // If the command has an argument and its value matches this button. == used for string/number comparison
+ if ( commandArr.length > 1 && document.queryCommandEnabled( command ) && document.queryCommandValue( command ) === commandArr[ 1 ] ) {
+ self.addClass( options.activeToolbarClass );
+ }
+
+ // Else if the command has no arguments and it is active
+ else if ( commandArr.length === 1 && document.queryCommandEnabled( command ) && document.queryCommandState( command ) ) {
+ self.addClass( options.activeToolbarClass );
+ }
+
+ // Else the command is not active
+ else {
+ self.removeClass( options.activeToolbarClass );
+ }
+ } );
+ }
+ };
+
+ Wysiwyg.prototype.execCommand = function( commandWithArgs, valueArg, editor, options, toolbarBtnSelector ) {
+ var commandArr = commandWithArgs.split( " " ),
+ command = commandArr.shift(),
+ args = commandArr.join( " " ) + ( valueArg || "" );
+
+ var parts = commandWithArgs.split( "-" );
+
+ if ( parts.length === 1 ) {
+ document.execCommand( command, false, args );
+ } else if ( parts[ 0 ] === "format" && parts.length === 2 ) {
+ document.execCommand( "formatBlock", false, parts[ 1 ] );
+ }
+
+ ( editor ).trigger( "change" );
+ this.updateToolbar( editor, toolbarBtnSelector, options );
+ };
+
+ Wysiwyg.prototype.bindHotkeys = function( editor, options, toolbarBtnSelector ) {
+ var self = this;
+ $.each( options.hotKeys, function( hotkey, command ) {
+ if(!command) return;
+
+ $( editor ).keydown( hotkey, function( e ) {
+ if ( editor.attr( "contenteditable" ) && $( editor ).is( ":visible" ) ) {
+ e.preventDefault();
+ e.stopPropagation();
+ self.execCommand( command, null, editor, options, toolbarBtnSelector );
+ }
+ } ).keyup( hotkey, function( e ) {
+ if ( editor.attr( "contenteditable" ) && $( editor ).is( ":visible" ) ) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ } );
+ } );
+
+ editor.keyup( function() { editor.trigger( "change" ); } );
+ };
+
+ Wysiwyg.prototype.getCurrentRange = function() {
+ var sel, range;
+ if ( window.getSelection ) {
+ sel = window.getSelection();
+ if ( sel.getRangeAt && sel.rangeCount ) {
+ range = sel.getRangeAt( 0 );
+ }
+ } else if ( document.selection ) {
+ range = document.selection.createRange();
+ }
+
+ return range;
+ };
+
+ Wysiwyg.prototype.saveSelection = function() {
+ this.selectedRange = this.getCurrentRange();
+ };
+
+ Wysiwyg.prototype.restoreSelection = function() {
+ var selection;
+ if ( window.getSelection || document.createRange ) {
+ selection = window.getSelection();
+ if ( this.selectedRange ) {
+ try {
+ selection.removeAllRanges();
+ }
+ catch ( ex ) {
+ document.body.createTextRange().select();
+ document.selection.empty();
+ }
+ selection.addRange( this.selectedRange );
+ }
+ } else if ( document.selection && this.selectedRange ) {
+ this.selectedRange.select();
+ }
+ };
+
+ // Adding Toggle HTML based on the work by @jd0000, but cleaned up a little to work in this context.
+ Wysiwyg.prototype.toggleHtmlEdit = function( editor ) {
+ if ( editor.data( "wysiwyg-html-mode" ) !== true ) {
+ var oContent = editor.html();
+ var editorPre = $( "
" );
+ $( editorPre ).append( document.createTextNode( oContent ) );
+ $( editorPre ).attr( "contenteditable", true );
+ $( editor ).html( " " );
+ $( editor ).append( $( editorPre ) );
+ $( editor ).attr( "contenteditable", false );
+ $( editor ).data( "wysiwyg-html-mode", true );
+ $( editorPre ).focus();
+ } else {
+ $( editor ).html( $( editor ).text() );
+ $( editor ).attr( "contenteditable", true );
+ $( editor ).data( "wysiwyg-html-mode", false );
+ $( editor ).focus();
+ }
+ };
+
+ Wysiwyg.prototype.insertFiles = function( files, options, editor, toolbarBtnSelector ) {
+ var self = this;
+ editor.focus();
+ $.each( files, function( idx, fileInfo ) {
+ if ( /^image\//.test( fileInfo.type ) ) {
+ $.when( self.readFileIntoDataUrl( fileInfo ) ).done( function( dataUrl ) {
+ self.execCommand( "insertimage", dataUrl, editor, options, toolbarBtnSelector );
+ editor.trigger( "image-inserted" );
+ } ).fail( function( e ) {
+ options.fileUploadError( "file-reader", e );
+ } );
+ } else {
+ options.fileUploadError( "unsupported-file-type", fileInfo.type );
+ }
+ } );
+ };
+
+ Wysiwyg.prototype.markSelection = function( input, color, options ) {
+ this.restoreSelection( );
+ if ( document.queryCommandSupported( "hiliteColor" ) ) {
+ document.execCommand( "hiliteColor", false, color || "transparent" );
+ }
+ this.saveSelection( );
+ input.data( options.selectionMarker, color );
+ };
+
+ Wysiwyg.prototype.bindToolbar = function( editor, toolbar, options, toolbarBtnSelector ) {
+ var self = this;
+ toolbar.find( toolbarBtnSelector ).click( function() {
+ self.restoreSelection( );
+ editor.focus();
+
+ if ( editor.data( options.commandRole ) === "html" ) {
+ self.toggleHtmlEdit( editor );
+ } else {
+ self.execCommand( $( this ).data( options.commandRole ), null, editor, options, toolbarBtnSelector );
+ }
+
+ self.saveSelection( );
+ } );
+
+ toolbar.find( "[data-toggle=dropdown]" ).click( this.restoreSelection( ) );
+
+ toolbar.find( "input[type=text][data-" + options.commandRole + "]" ).on( "webkitspeechchange change", function() {
+ var newValue = this.value; // Ugly but prevents fake double-calls due to selection restoration
+ this.value = "";
+ self.restoreSelection( );
+ if ( newValue ) {
+ editor.focus();
+ self.execCommand( $( this ).data( options.commandRole ), newValue, editor, options, toolbarBtnSelector );
+ }
+ self.saveSelection( );
+ } ).on( "focus", function() {
+ var input = $( this );
+ if ( !input.data( options.selectionMarker ) ) {
+ self.markSelection( input, options.selectionColor, options );
+ input.focus();
+ }
+ } ).on( "blur", function() {
+ var input = $( this );
+ if ( input.data( options.selectionMarker ) ) {
+ self.markSelection( input, false, options );
+ }
+ } );
+ toolbar.find( "input[type=file][data-" + options.commandRole + "]" ).change( function() {
+ self.restoreSelection( );
+ if ( this.type === "file" && this.files && this.files.length > 0 ) {
+ self.insertFiles( this.files, options, editor, toolbarBtnSelector );
+ }
+ self.saveSelection( );
+ this.value = "";
+ } );
+ };
+
+ Wysiwyg.prototype.initFileDrops = function( editor, options, toolbarBtnSelector ) {
+ var self = this;
+ editor.on( "dragenter dragover", false ).on( "drop", function( e ) {
+ var dataTransfer = e.originalEvent.dataTransfer;
+ e.stopPropagation();
+ e.preventDefault();
+ if ( dataTransfer && dataTransfer.files && dataTransfer.files.length > 0 ) {
+ self.insertFiles( dataTransfer.files, options, editor, toolbarBtnSelector );
+ }
+ } );
+ };
+
+ /*
+ * Represenets an editor
+ * @constructor
+ * @param {object} userOptions - The default options selected by the user.
+ */
+
+ $.fn.wysiwyg = function( userOptions ) {
+ var wysiwyg = new Wysiwyg( this, userOptions );
+ };
+
+} )( window, window.jQuery );
diff --git a/assets/3rdparty/jquery.hotkeys.js b/assets/3rdparty/jquery.hotkeys.js
new file mode 100644
index 00000000..e7701f39
--- /dev/null
+++ b/assets/3rdparty/jquery.hotkeys.js
@@ -0,0 +1,204 @@
+/*jslint browser: true*/
+/*jslint jquery: true*/
+
+/*
+ * jQuery Hotkeys Plugin
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Based upon the plugin by Tzury Bar Yochay:
+ * https://github.com/tzuryby/jquery.hotkeys
+ *
+ * Original idea by:
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
+ */
+
+/*
+ * One small change is: now keys are passed by object { keys: '...' }
+ * Might be useful, when you want to pass some other data to your handler
+ */
+
+(function(jQuery) {
+
+ jQuery.hotkeys = {
+ version: "0.2.0",
+
+ specialKeys: {
+ 8: "backspace",
+ 9: "tab",
+ 10: "return",
+ 13: "return",
+ 16: "shift",
+ 17: "ctrl",
+ 18: "alt",
+ 19: "pause",
+ 20: "capslock",
+ 27: "esc",
+ 32: "space",
+ 33: "pageup",
+ 34: "pagedown",
+ 35: "end",
+ 36: "home",
+ 37: "left",
+ 38: "up",
+ 39: "right",
+ 40: "down",
+ 45: "insert",
+ 46: "del",
+ 59: ";",
+ 61: "=",
+ 96: "0",
+ 97: "1",
+ 98: "2",
+ 99: "3",
+ 100: "4",
+ 101: "5",
+ 102: "6",
+ 103: "7",
+ 104: "8",
+ 105: "9",
+ 106: "*",
+ 107: "+",
+ 109: "-",
+ 110: ".",
+ 111: "/",
+ 112: "f1",
+ 113: "f2",
+ 114: "f3",
+ 115: "f4",
+ 116: "f5",
+ 117: "f6",
+ 118: "f7",
+ 119: "f8",
+ 120: "f9",
+ 121: "f10",
+ 122: "f11",
+ 123: "f12",
+ 144: "numlock",
+ 145: "scroll",
+ 173: "-",
+ 186: ";",
+ 187: "=",
+ 188: ",",
+ 189: "-",
+ 190: ".",
+ 191: "/",
+ 192: "`",
+ 219: "[",
+ 220: "\\",
+ 221: "]",
+ 222: "'"
+ },
+
+ shiftNums: {
+ "`": "~",
+ "1": "!",
+ "2": "@",
+ "3": "#",
+ "4": "$",
+ "5": "%",
+ "6": "^",
+ "7": "&",
+ "8": "*",
+ "9": "(",
+ "0": ")",
+ "-": "_",
+ "=": "+",
+ ";": ": ",
+ "'": "\"",
+ ",": "<",
+ ".": ">",
+ "/": "?",
+ "\\": "|"
+ },
+
+ // excludes: button, checkbox, file, hidden, image, password, radio, reset, search, submit, url
+ textAcceptingInputTypes: [
+ "text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime",
+ "datetime-local", "search", "color", "tel"],
+
+ // default input types not to bind to unless bound directly
+ textInputTypes: /textarea|input|select/i,
+
+ options: {
+ filterInputAcceptingElements: true,
+ filterTextInputs: true,
+ filterContentEditable: true
+ }
+ };
+
+ function keyHandler(handleObj) {
+ if (typeof handleObj.data === "string") {
+ handleObj.data = {
+ keys: handleObj.data
+ };
+ }
+
+ // Only care when a possible input has been specified
+ if (!handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string") {
+ return;
+ }
+
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.keys.toLowerCase().split(" ");
+
+ handleObj.handler = function(event) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if (this !== event.target &&
+ (jQuery.hotkeys.options.filterInputAcceptingElements &&
+ jQuery.hotkeys.textInputTypes.test(event.target.nodeName) ||
+ (jQuery.hotkeys.options.filterContentEditable && jQuery(event.target).attr('contenteditable')) ||
+ (jQuery.hotkeys.options.filterTextInputs &&
+ jQuery.inArray(event.target.type, jQuery.hotkeys.textAcceptingInputTypes) > -1))) {
+ return;
+ }
+
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[event.which],
+ character = String.fromCharCode(event.which).toLowerCase(),
+ modif = "",
+ possible = {};
+
+ jQuery.each(["alt", "ctrl", "shift"], function(index, specialKey) {
+
+ if (event[specialKey + 'Key'] && special !== specialKey) {
+ modif += specialKey + '+';
+ }
+ });
+
+ // metaKey is triggered off ctrlKey erronously
+ if (event.metaKey && !event.ctrlKey && special !== "meta") {
+ modif += "meta+";
+ }
+
+ if (event.metaKey && special !== "meta" && modif.indexOf("alt+ctrl+shift+") > -1) {
+ modif = modif.replace("alt+ctrl+shift+", "hyper+");
+ }
+
+ if (special) {
+ possible[modif + special] = true;
+ }
+ else {
+ possible[modif + character] = true;
+ possible[modif + jQuery.hotkeys.shiftNums[character]] = true;
+
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if (modif === "shift+") {
+ possible[jQuery.hotkeys.shiftNums[character]] = true;
+ }
+ }
+
+ for (var i = 0, l = keys.length; i < l; i++) {
+ if (possible[keys[i]]) {
+ return origHandler.apply(this, arguments);
+ }
+ }
+ };
+ }
+
+ jQuery.each(["keydown", "keyup", "keypress"], function() {
+ jQuery.event.special[this] = {
+ add: keyHandler
+ };
+ });
+
+})(jQuery || this.jQuery || window.jQuery);
diff --git a/assets/css/components/posts/form.css b/assets/css/components/posts/form.css
new file mode 100644
index 00000000..73efd881
--- /dev/null
+++ b/assets/css/components/posts/form.css
@@ -0,0 +1,15 @@
+/**
+ * Posts creation form
+ *
+ * @author Pierre HUBERT
+ */
+
+.post-form .new-message {
+ width: 100%;
+ min-height: 100px;
+ font-size: 14px;
+ line-height: 18px;
+ border: 1px solid #dddddd;
+ padding: 10px;
+ margin-bottom: 10px;
+}
\ No newline at end of file
diff --git a/assets/js/common/functionsSchema.js b/assets/js/common/functionsSchema.js
index a551062d..f22030c1 100644
--- a/assets/js/common/functionsSchema.js
+++ b/assets/js/common/functionsSchema.js
@@ -655,9 +655,16 @@ var ComunicWeb = {
* Posts UI
*/
ui: {
-
+ //TODO : implement
},
+ /**
+ * Posts creation form
+ */
+ form: {
+ //TODO : implement
+ }
+
},
/**
diff --git a/assets/js/components/posts/form.js b/assets/js/components/posts/form.js
new file mode 100644
index 00000000..12021012
--- /dev/null
+++ b/assets/js/components/posts/form.js
@@ -0,0 +1,55 @@
+/**
+ * Posts creation form
+ *
+ * @author Pierre HUBERT
+ */
+
+ComunicWeb.components.posts.form = {
+
+ /**
+ * Display post creation form
+ *
+ * @param {string} kind The kind of page
+ * @param {int} id The ID of the page
+ * @param {HTMLElement} target The target of the form
+ */
+ display: function(kind, id, target){
+
+ //Log action
+ ComunicWeb.debug.logMessage("Display post creation form");
+
+ //Create form creation box
+ var boxRoot = createElem2({
+ appendTo: target,
+ type: "div",
+ class: "box box-primary post-form"
+ });
+
+ //Create box body
+ var boxBody = createElem2({
+ appendTo: boxRoot,
+ type: "div",
+ class: "box-body"
+ });
+
+ //Create post message textarea
+ var inputMessageDiv = createElem2({
+ appendTo: boxBody,
+ type: "div",
+ class: "new-message",
+ });
+
+ //Enable bootstrap-wysiwyg
+ $(inputMessageDiv).wysiwyg();
+
+
+ //Add send button
+ var sendButton = createElem2({
+ appendTo: boxBody,
+ type: "button",
+ class: "btn btn-primary pull-right",
+ innerHTML: "Send"
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/assets/js/pages/userPage/main.js b/assets/js/pages/userPage/main.js
index 54e04557..21781e97 100644
--- a/assets/js/pages/userPage/main.js
+++ b/assets/js/pages/userPage/main.js
@@ -154,6 +154,10 @@ ComunicWeb.pages.userPage.main = {
class: "col-md-6"
});
+ //Display post creation form if the user is allowed to do so
+ if(infos.can_post_texts == true)
+ ComunicWeb.components.posts.form.display("user", infos.userID, rightColumn);
+
//Display posts
ComunicWeb.pages.userPage.posts.display(infos, params, rightColumn);
}
diff --git a/system/config/dev.config.php b/system/config/dev.config.php
index c4ea6993..923cf790 100644
--- a/system/config/dev.config.php
+++ b/system/config/dev.config.php
@@ -83,6 +83,12 @@ class Dev {
//ChartJS
"3rdparty/adminLTE/plugins/chartjs/Chart.min.js",
+ //Jquery hotkeys
+ "3rdparty/jquery.hotkeys.js",
+
+ //Bootstrap-WYSIWYG
+ "3rdparty/bootstrap-wysiwyg.js",
+
//VideoJS
//"3rdparty/videojs/6.4.0/video.min.js"
);
@@ -119,6 +125,7 @@ class Dev {
//Posts component
"css/components/posts/ui.css",
+ "css/components/posts/form.css",
//Pages stylesheets
//User Page
@@ -200,6 +207,7 @@ class Dev {
//Posts component
"js/components/posts/interface.js",
"js/components/posts/ui.js",
+ "js/components/posts/form.js",
//Modern textarea handler
"js/components/textarea.js",