From bf5f9938b90dfa023c314e9c85bdda1863f406c1 Mon Sep 17 00:00:00 2001 From: Charles du Jeu Date: Sun, 7 Jul 2013 18:46:32 +0200 Subject: [PATCH] Etherpad simple implementation --- .../editor.etherpad/class.EtherpadClient.php | 90 +++++ .../editor.etherpad/class.EtherpadLauncher.js | 87 +++++ .../etherpad-client/etherpad-lite-client.php | 360 ++++++++++++++++++ .../plugins/editor.etherpad/i18n/conf/en.php | 24 ++ .../plugins/editor.etherpad/i18n/conf/fr.php | 25 ++ core/src/plugins/editor.etherpad/i18n/en.php | 6 + core/src/plugins/editor.etherpad/manifest.xml | 68 ++++ .../plugins/editor.etherpad/plugin_doc.html | 1 + 8 files changed, 661 insertions(+) create mode 100644 core/src/plugins/editor.etherpad/class.EtherpadClient.php create mode 100644 core/src/plugins/editor.etherpad/class.EtherpadLauncher.js create mode 100755 core/src/plugins/editor.etherpad/etherpad-client/etherpad-lite-client.php create mode 100644 core/src/plugins/editor.etherpad/i18n/conf/en.php create mode 100644 core/src/plugins/editor.etherpad/i18n/conf/fr.php create mode 100644 core/src/plugins/editor.etherpad/i18n/en.php create mode 100644 core/src/plugins/editor.etherpad/manifest.xml create mode 100644 core/src/plugins/editor.etherpad/plugin_doc.html diff --git a/core/src/plugins/editor.etherpad/class.EtherpadClient.php b/core/src/plugins/editor.etherpad/class.EtherpadClient.php new file mode 100644 index 0000000000..bda7f9b488 --- /dev/null +++ b/core/src/plugins/editor.etherpad/class.EtherpadClient.php @@ -0,0 +1,90 @@ +detectStreamWrapper(false)){ + return false; + } + $plugin = AJXP_PluginsService::findPlugin("access", $repository->getAccessType()); + $streamData = $plugin->detectStreamWrapper(true); + $destStreamURL = $streamData["protocol"]."://".$repository->getId()."/"; + $filename = $destStreamURL. AJXP_Utils::securePath($httpVars["file"]); + + if(!is_file($filename)){ + throw new Exception("Cannot find file!"); + } + } + + require_once("etherpad-client/etherpad-lite-client.php"); + $client = new EtherpadLiteClient("nl8VJIWXZMHNj7aWj6rWy4CLct1mu97v",$this->baseURL."/api"); + + if($actionName == "etherpad_create"){ + + if(isSet($httpVars["pad_name"])){ + + $padID = $httpVars["pad_name"]; + $startContent = ""; + + }else if(isSet($httpVars["file"])){ + + $startContent = file_get_contents($filename); + $padID = AJXP_Utils::slugify($httpVars["file"]); + + } + + $userName = AuthService::getLoggedUser()->getId(); + + $res = $client->createAuthorIfNotExistsFor($userName, $userName); + $authorID = $res->authorID; + + $res2 = $client->createGroupIfNotExistsFor("ajaxplorer"); + $groupID = $res2->groupID; + + $resP = $client->listPads($res2->groupID); + + $pads = $resP->padIDs; + if(!in_array($groupID.'$'.$padID, $pads)){ + $res3 = $client->createGroupPad($groupID, $padID, $startContent); + } + + $res4 = $client->createSession($groupID, $authorID, time()+14400); + $sessionID = $res4->sessionID; + + setcookie('sessionID', $sessionID, null, "/"); + + $padID = $groupID.'$'.$padID; + + $data = array( + "url" => $this->baseURL."/p/".$padID, + "padID" => $padID, + "sessionID" => $sessionID, + ); + + HTMLWriter::charsetHeader('application/json'); + echo(json_encode($data)); + + }else if ($actionName == "etherpad_save"){ + + $padID = $httpVars["pad_id"]; + $res = $client->getText($padID); + file_put_contents($filename, $res->text); + + }else if ($actionName == "etherpad_close"){ + + // WE SHOULD DETECT IF THERE IS NOBODY CONNECTED ANYMORE, AND DELETE THE PAD. + $sessionID = $httpVars["session_id"]; + $i = $client->getSessionInfo($sessionID); + + } + + } + + +} \ No newline at end of file diff --git a/core/src/plugins/editor.etherpad/class.EtherpadLauncher.js b/core/src/plugins/editor.etherpad/class.EtherpadLauncher.js new file mode 100644 index 0000000000..937b8f917e --- /dev/null +++ b/core/src/plugins/editor.etherpad/class.EtherpadLauncher.js @@ -0,0 +1,87 @@ +/* + * Copyright 2007-2011 Charles du Jeu + * This file is part of AjaXplorer. + * + * AjaXplorer is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AjaXplorer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with AjaXplorer. If not, see . + * + * The latest code can be found at . + */ +Class.create("EtherpadLauncher", AbstractEditor, { + + padID:null, + sessionID: null, + node: null, + + initialize: function($super, oFormObject) + { + $super(oFormObject); + if(!ajaxplorer.user || ajaxplorer.user.canWrite()){ + this.canWrite = true; + this.actions.get("saveButton").observe('click', function(){ + this.saveFile(); + return false; + }.bind(this)); + }else{ + this.canWrite = false; + this.actions.get("saveButton").hide(); + } + this.actions.get("downloadFileButton").observe('click', function(){ + if(!this.currentFile) return; + ajaxplorer.triggerDownload(ajxpBootstrap.parameters.get('ajxpServerAccess')+'&action=download&file='+this.currentFile); + return false; + }.bind(this)); + }, + + + open : function($super, nodeOrNodes){ + $super(nodeOrNodes); + this.node = nodeOrNodes; + var fileName = nodeOrNodes.getPath(); + var conn = new Connexion(); + conn.addParameter("get_action", "etherpad_create"); + conn.addParameter("file", fileName); + conn.onComplete = function(transport){ + var data = transport.responseJSON; + this.padID = data.padID; + this.sessionID = data.sessionID; + $("ether_box").down("#ether_box_frame").src = data.url; + fitHeightToBottom($("ether_box").down("#ether_box_frame")); + }.bind(this); + conn.sendAsync(); + + if(window.ajxpMobile){ + this.setFullScreen(); + attachMobileScroll(this.textarea, "vertical"); + } + this.element.observeOnce("editor:close", function(){ + var conn = new Connexion(); + conn.addParameter("get_action", "etherpad_close"); + conn.addParameter("file", this.node.getPath()); + conn.addParameter("pad_id", this.padID); + conn.addParameter("session_id", this.sessionID); + conn.sendAsync(); + }); + }, + + + saveFile : function(){ + if(!this.padID) return; + var conn = new Connexion(); + conn.addParameter("get_action", "etherpad_save"); + conn.addParameter("file", this.node.getPath()); + conn.addParameter("pad_id", this.padID); + conn.sendAsync(); + } + +}); \ No newline at end of file diff --git a/core/src/plugins/editor.etherpad/etherpad-client/etherpad-lite-client.php b/core/src/plugins/editor.etherpad/etherpad-client/etherpad-lite-client.php new file mode 100755 index 0000000000..db312ab7f0 --- /dev/null +++ b/core/src/plugins/editor.etherpad/etherpad-client/etherpad-lite-client.php @@ -0,0 +1,360 @@ +apiKey = $apiKey; + + if (isset($baseUrl)){ + $this->baseUrl = $baseUrl; + } + if (!filter_var($this->baseUrl, FILTER_VALIDATE_URL)){ + throw new InvalidArgumentException("[{$this->baseUrl}] is not a valid URL"); + } + } + + protected function get($function, array $arguments = array()){ + return $this->call($function, $arguments, 'GET'); + } + + protected function post($function, array $arguments = array()){ + return $this->call($function, $arguments, 'POST'); + } + + protected function call($function, array $arguments = array(), $method = 'GET'){ + $arguments['apikey'] = $this->apiKey; + $arguments = http_build_query($arguments, '', '&'); + $url = $this->baseUrl."/".self::API_VERSION."/".$function; + if ($method !== 'POST'){ + $url .= "?".$arguments; + } + // use curl of it's available + if (function_exists('curl_init')){ + $c = curl_init($url); + curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + curl_setopt($c, CURLOPT_TIMEOUT, 20); + if ($method === 'POST'){ + curl_setopt($c, CURLOPT_POST, true); + curl_setopt($c, CURLOPT_POSTFIELDS, $arguments); + } + $result = curl_exec($c); + curl_close($c); + // fallback to plain php + } else { + $params = array('http' => array('method' => $method, 'ignore_errors' => true, 'header' => 'Content-Type:application/x-www-form-urlencoded')); + if ($method === 'POST'){ + $params['http']['content'] = $arguments; + } + $context = stream_context_create($params); + $fp = fopen($url, 'rb', false, $context); + $result = $fp ? stream_get_contents($fp) : null; + } + + if(!$result){ + throw new UnexpectedValueException("Empty or No Response from the server"); + } + + $result = json_decode($result); + if ($result === null){ + throw new UnexpectedValueException("JSON response could not be decoded"); + } + return $this->handleResult($result); + } + + protected function handleResult($result){ + if (!isset($result->code)){ + throw new RuntimeException("API response has no code"); + } + if (!isset($result->message)){ + throw new RuntimeException("API response has no message"); + } + if (!isset($result->data)){ + $result->data = null; + } + + switch ($result->code){ + case self::CODE_OK: + return $result->data; + case self::CODE_INVALID_PARAMETERS: + case self::CODE_INVALID_API_KEY: + throw new InvalidArgumentException($result->message); + case self::CODE_INTERNAL_ERROR: + throw new RuntimeException($result->message); + case self::CODE_INVALID_FUNCTION: + throw new BadFunctionCallException($result->message); + default: + throw new RuntimeException("An unexpected error occurred whilst handling the response"); + } + } + + // GROUPS + // Pads can belong to a group. There will always be public pads that doesnt belong to a group (or we give this group the id 0) + + // creates a new group + public function createGroup(){ + return $this->post("createGroup"); + } + + // this functions helps you to map your application group ids to etherpad lite group ids + public function createGroupIfNotExistsFor($groupMapper){ + return $this->post("createGroupIfNotExistsFor", array( + "groupMapper" => $groupMapper + )); + } + + // deletes a group + public function deleteGroup($groupID){ + return $this->post("deleteGroup", array( + "groupID" => $groupID + )); + } + + // returns all pads of this group + public function listPads($groupID){ + return $this->get("listPads", array( + "groupID" => $groupID + )); + } + + // creates a new pad in this group + public function createGroupPad($groupID, $padName, $text){ + return $this->post("createGroupPad", array( + "groupID" => $groupID, + "padName" => $padName, + "text" => $text + )); + } + + // list all groups + public function listAllGroups(){ + return $this->get("listAllGroups"); + } + + // AUTHORS + // Theses authors are bind to the attributes the users choose (color and name). + + // creates a new author + public function createAuthor($name){ + return $this->post("createAuthor", array( + "name" => $name + )); + } + + // this functions helps you to map your application author ids to etherpad lite author ids + public function createAuthorIfNotExistsFor($authorMapper, $name){ + return $this->post("createAuthorIfNotExistsFor", array( + "authorMapper" => $authorMapper, + "name" => $name + )); + } + + // returns the ids of all pads this author as edited + public function listPadsOfAuthor($authorID){ + return $this->get("listPadsOfAuthor", array( + "authorID" => $authorID + )); + } + + // Gets an author's name + public function getAuthorName($authorID){ + return $this->get("getAuthorName", array( + "authorID" => $authorID + )); + } + + // SESSIONS + // Sessions can be created between a group and a author. This allows + // an author to access more than one group. The sessionID will be set as + // a cookie to the client and is valid until a certian date. + + // creates a new session + public function createSession($groupID, $authorID, $validUntil){ + return $this->post("createSession", array( + "groupID" => $groupID, + "authorID" => $authorID, + "validUntil" => $validUntil + )); + } + + // deletes a session + public function deleteSession($sessionID){ + return $this->post("deleteSession", array( + "sessionID" => $sessionID + )); + } + + // returns informations about a session + public function getSessionInfo($sessionID){ + return $this->get("getSessionInfo", array( + "sessionID" => $sessionID + )); + } + + // returns all sessions of a group + public function listSessionsOfGroup($groupID){ + return $this->get("listSessionsOfGroup", array( + "groupID" => $groupID + )); + } + + // returns all sessions of an author + public function listSessionsOfAuthor($authorID){ + return $this->get("listSessionsOfAuthor", array( + "authorID" => $authorID + )); + } + + // PAD CONTENT + // Pad content can be updated and retrieved through the API + + // returns the text of a pad + public function getText($padID, $rev=null){ + $params = array("padID" => $padID); + if (isset($rev)){ + $params["rev"] = $rev; + } + return $this->get("getText", $params); + } + + // returns the text of a pad as html + public function getHTML($padID, $rev=null){ + $params = array("padID" => $padID); + if (isset($rev)){ + $params["rev"] = $rev; + } + return $this->get("getHTML", $params); + } + + // sets the text of a pad + public function setText($padID, $text){ + return $this->post("setText", array( + "padID" => $padID, + "text" => $text + )); + } + + // sets the html text of a pad + public function setHTML($padID, $html){ + return $this->post("setHTML", array( + "padID" => $padID, + "html" => $html + )); + } + + // PAD + // Group pads are normal pads, but with the name schema + // GROUPID$PADNAME. A security manager controls access of them and its + // forbidden for normal pads to include a $ in the name. + + // creates a new pad + public function createPad($padID, $text){ + return $this->post("createPad", array( + "padID" => $padID, + "text" => $text + ), 'POST'); + } + + // returns the number of revisions of this pad + public function getRevisionsCount($padID){ + return $this->get("getRevisionsCount", array( + "padID" => $padID + )); + } + + // returns the number of users currently editing this pad + public function padUsersCount($padID){ + return $this->get("padUsersCount", array( + "padID" => $padID + )); + } + + // return the time the pad was last edited as a Unix timestamp + public function getLastEdited($padID){ + return $this->get("getLastEdited", array( + "padID" => $padID + )); + } + + // deletes a pad + public function deletePad($padID){ + return $this->post("deletePad", array( + "padID" => $padID + )); + } + + // returns the read only link of a pad + public function getReadOnlyID($padID){ + return $this->get("getReadOnlyID", array( + "padID" => $padID + )); + } + + // returns the ids of all authors who've edited this pad + public function listAuthorsOfPad($padID){ + return $this->get("listAuthorsOfPad", array( + "padID" => $padID + )); + } + + // sets a boolean for the public status of a pad + public function setPublicStatus($padID, $publicStatus){ + if (is_bool($publicStatus)) { + $publicStatus = $publicStatus ? "true" : "false"; + } + return $this->post("setPublicStatus", array( + "padID" => $padID, + "publicStatus" => $publicStatus + )); + } + + // return true of false + public function getPublicStatus($padID){ + return $this->get("getPublicStatus", array( + "padID" => $padID + )); + } + + // returns ok or a error message + public function setPassword($padID, $password){ + return $this->post("setPassword", array( + "padID" => $padID, + "password" => $password + )); + } + + // returns true or false + public function isPasswordProtected($padID){ + return $this->get("isPasswordProtected", array( + "padID" => $padID + )); + } + + // Get pad users + public function padUsers($padID){ + return $this->get("padUsers", array( + "padID" => $padID + )); + } + + // Send all clients a message + public function sendClientsMessage($padID, $msg){ + return $this->post("sendClientsMessage", array( + "padID" => $padID, + "msg" => $msg + )); + } +} + diff --git a/core/src/plugins/editor.etherpad/i18n/conf/en.php b/core/src/plugins/editor.etherpad/i18n/conf/en.php new file mode 100644 index 0000000000..06f408faf8 --- /dev/null +++ b/core/src/plugins/editor.etherpad/i18n/conf/en.php @@ -0,0 +1,24 @@ + +* This file is part of AjaXplorer. +* +* AjaXplorer is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* AjaXplorer is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with AjaXplorer. If not, see . +* +* The latest code can be found at . +*/ +$mess=array( +"Etherpad" => "Etherpad", +"Collaborative edition of text files" => "Collaborative edition of text files", +); diff --git a/core/src/plugins/editor.etherpad/i18n/conf/fr.php b/core/src/plugins/editor.etherpad/i18n/conf/fr.php new file mode 100644 index 0000000000..7fbbd8a3a5 --- /dev/null +++ b/core/src/plugins/editor.etherpad/i18n/conf/fr.php @@ -0,0 +1,25 @@ + +* This file is part of AjaXplorer. +* +* AjaXplorer is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* AjaXplorer is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with AjaXplorer. If not, see . +* +* The latest code can be found at . +*/ +$mess=array( +"Etherpad" => "Etherpad", +"Collaborative edition of text files" => "Edition collaborative des fichiers", +); +?> \ No newline at end of file diff --git a/core/src/plugins/editor.etherpad/i18n/en.php b/core/src/plugins/editor.etherpad/i18n/en.php new file mode 100644 index 0000000000..06f9750f04 --- /dev/null +++ b/core/src/plugins/editor.etherpad/i18n/en.php @@ -0,0 +1,6 @@ + "Create an empty Pad", + "2" => "Collaborative Editor", + "3" => "Real-time collaborative edition" +); \ No newline at end of file diff --git a/core/src/plugins/editor.etherpad/manifest.xml b/core/src/plugins/editor.etherpad/manifest.xml new file mode 100644 index 0000000000..bc14b3b3d0 --- /dev/null +++ b/core/src/plugins/editor.etherpad/manifest.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/plugins/editor.etherpad/plugin_doc.html b/core/src/plugins/editor.etherpad/plugin_doc.html new file mode 100644 index 0000000000..175cedbdc6 --- /dev/null +++ b/core/src/plugins/editor.etherpad/plugin_doc.html @@ -0,0 +1 @@ +

Basic editor for all text files. No specific configuration here, allows you to edit online a given file and save the modifications, without having to download it to your desktop and re-upload it. Warning, it’s the « parent » of other text edition editors, like codepress and ckeditor, thus do not remove it even you don’t use it!

\ No newline at end of file