Mostrar conocimiento avanzadoOcultar conocimiento avanzadoCloud-Module Plugins
Will be abandoned with ILIAS 8
Introduction
The main purpose of the Cloud-Module is to offer developers of plugins the possibility to embed a remote file system into ILIAS with the least amount of effort possible through the implementation of so called Cloud-Plugins. Further, developers should have the chance to include custom functionality to their plugins to go beyond a simple display of files and folders. The following picture visualizes the main idea of the Cloud-Module:
Basic Setup
The first step in developing a new Cloud-Plugin is to add the following structure to the customizing folder in the ILIAS main directory:
- Create the folder: “Customizing/global/plugins/Modules/Cloud/CloudHook/ExampleCloud”
- Inside this folder create the usual files similar to what is known from the “RepositoryObject plugins”. That is the file “plugin.php”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php // alphanumerical ID of the plugin; never change this $id = "excd"; // code version; must be changed for all code changes $version = "0.0.1"; // ilias min and max version; must always reflect the versions that should // run with the plugin $ilias_min_version = "4.4.0"; $ilias_max_version = "4.4.999"; // optional, but useful: Add one or more responsible persons and a contact email $responsible = "Timon Amstutz"; $responsible_mail = "timon.amstutz@ilub.unibe.ch"; ?> |
|
- The file “sql/dbupdate.php” which can be left empty for now.
- The file “lang/ilias_en.lang” (and other language files as needed) which should contain at least one entry for the name of the plugin and one some information about the plugin (this is not mandatory for the plugin to run without error):
1 2 | ExampleCloud#:#Example Cloud create_info#:#Some Information which is displayed to the user when creating a new Cloud-Object |
|
- As last step for the basic setup the file “classes/class.ExampleCloudPlugin.php” must be created. It is important, that the class name follows the convention “class.YourPluginNamePlugin.php” in order to be found. Also the name returned by “getPluginName” must match the name used in the language file in order to get translated correctly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?php include_once("./Modules/Cloud/classes/class.ilCloudHookPlugin.php"); /** * * @author Timon Amstutz timon.amstutz@ilub.unibe.ch * @version $Id$ * */ class ilExampleCloudPlugin extends ilCloudHookPlugin { /** * @return string */ public function getPluginName() { return "ExampleCloud"; } } ?> |
|
After completing these steps the plugin should now appear in the plugin section of the administration in the row “CloudHook, Modules/Cloud” of the plugin slot table (“Administration->Plugins, Modules and Services->Plugins”). When you click on administrate you should see your plugin and the file “plugin.php” as well as the class file should be marked as “Available”. Also the language file should show up in the third column. If so, the plugin can be updated and activated. It is now technically working but has yet no practical functionality.
Important note: The Cloud-Module is deactivated by default. In order to create a Cloud-Object (which is what the Cloud-Plugins essentially are) the Cloud-Modules must be activated. Uncheck therefore the “Disable Creation” box of the Cloud Module in the modules section (“Administration->Plugins, Modules and Services->Modules”).
Service Class
Up to now the plugin is empty. To actually display files from a remote server a class for communicating with some API or something similar from the remote system is needed. The Cloud-Module includes several classes which can be extended to add functionality to the plugin. The Cloud-Module always checks when instantiating such a class if the plugin extends it, if so, it uses the extended version of the plugin, if not, the version of the core-module. One such example is the “ilCloudPluginService” class which is responsible for the communication with the remote file system. The next step is to create the “ilExampleCloudService” class (again, the correct naming is important for the base class to find the correct class, the convention for service classes is: “ilYourPluginNameService”). In order to have a working example of a service class, our example service class implements a connection to an FTP-Server. The first Version of the “ilExampleCloudService” class looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php include_once("./Modules/Cloud/classes/class.ilCloudPluginService.php"); include_once('./Modules/Cloud/exceptions/class.ilCloudException.php'); include_once("./Modules/Cloud/classes/class.ilCloudUtil.php"); /** * * @author Timon Amstutz timon.amstutz@ilub.unibe.ch * @version $Id$ * */ class ilExampleCloudService extends ilCloudPluginService { } ?> |
|
If the plugin is run now, it still just displays the shell that is given from the core-module. The important methods to override from the base class in order for the plugin to work are:
1 2 3 4 5 6 7 | public function addToFileTree(ilCloudFileTree &$file_tree, $parent_folder = "/"){} public function getFile($path = null, ilCloudFileTree $file_tree = null){} public function createFolder($path = null, ilCloudFileTree $file_tree = null){} public function deleteItem($path = null, ilCloudFileTree $file_tree = null){} |
Before we implement these methods, lets write a little method to establish the connection with the ftp server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /** * @var resource */ protected $connection; /** * @var string */ protected $user_name; /** * @return resource * @throws ilCloudException */ public function getConnection() { if (!$this->connection) { $url = "xxx.xxxx.ch"; $this->user_name = "xxx"; $password = "xxx"; $this->connection = @ftp_connect(htmlspecialchars(htmlspecialchars_decode($url))); if (!$this->connection) { throw new ilCloudException(ilCloudException::PERMISSION_DENIED, "unknown address: " . $url); } $login_result = @ftp_login($this->connection, $this->user_name, $password); if (!$login_result) { throw new ilCloudException(ilCloudException::AUTHENTIFICATION_FAILED); } } return $this->connection; } |
Note the exception that is thrown if the connection or the login are not successful: The exception are instances of the “ilCloudException” class which takes two arguments in the constructor. The “const exception number” and some additional information as string.
Now the methods can be implemented with help of “getConnection()”. The first function to implement will normally be “addToFileTree”. This is also the one that will give the most work to do. Therefore in this example this function is done lastly. An easier function to start is “createFolder”:
1 2 3 4 5 6 7 8 9 10 11 12 13 | /** * @param $path * @param ilCloudFileTree $file_tree * @throws ilCloudException */ public function createFolder($rel_path = null, ilCloudFileTree $file_tree = null) { $path = ilCloudUtil::joinPathsAbsolute($file_tree->getRootPath(), $rel_path); if (!@ftp_mkdir($this->getConnection(), $path)) { throw new ilCloudException(ilCloudException::FOLDER_ALREADY_EXISTING_ON_SERVICE, $rel_path); } } |
The function takes to arguments: The “rel_path” of the folder we want to create and the “file_tree”. “rel_path” is the path of the folder relatively from the Root Path which can be set by the user in the “Settings” section. This “root_path” is stored in the “file_tree”. The “file_tree” represents the data loaded from a remote file system locally. It will be explained in a bit more detailed later. From the “root_path” and the “rel_path” we construct the path, which leads to the correct place on the server by simply joining them. The rest of the function is basically the call to “ftp_mkdir” and throwing an error if the call fails.
The method “putFile” works similarly. The only difference is the handle for the file we first create with the path to it, that we get as argument:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * @param $file * @param $name * @param string $path * @param null $file_tree * @throws ilCloudException */ public function putFile($file, $name, $rel_path = '', $file_tree = null) { $handle = fopen($file, 'r'); $path = ilCloudUtil::joinPathsAbsolute($file_tree->getRootPath(), $rel_path); if (!@ftp_fput($this->getConnection(), $path . "/" . utf8_encode($name), $handle, FTP_BINARY)) { throw new ilCloudException(ilCloudException::UPLOAD_FAILED, $rel_path); } } |
The method for deleting files and folders looks also familiar. To call the correct ftp function we check if the given path points to a file or a folder. This can be done by getting the $node (the representation of the file in file_tree) out of the file_tree and call its method getIsDir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /** * @param $path * @param ilCloudFileTree $file_tree * @throws ilCloudException */ public function deleteItem($rel_path = null, ilCloudFileTree $file_tree = null) { $path = ilCloudUtil::joinPathsAbsolute($file_tree->getRootPath(), $rel_path); $node = $file_tree->getNodeFromPath($rel_path); if($node->getIsDir()) { //Only works on empty folders if (!@ftp_rmdir($this->getConnection(), $path)) { throw new ilCloudException(ilCloudException::DELETE_FAILED, $rel_path); } } else { if (!@ftp_delete($this->getConnection(), $path)) { throw new ilCloudException(ilCloudException::DELETE_FAILED, $rel_path); } } } |
Please note, that the function only works on empty folders. A recursive call to delete the complete folder content would probably make sense here. This is only supposed to be an example, not a productive version, therefore this is omitted here.
With the method “getFile” we enable the download of files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /** * @param null $path * @param ilCloudFileTree $file_tree * @throws ilCloudException */ public function getFile($rel_path = null, ilCloudFileTree $file_tree = null) { $path = ilCloudUtil::joinPathsAbsolute($file_tree->getRootPath(), $rel_path); $tmp_name = tempnam(sys_get_temp_dir(), "cld_cldh_cldp_"); $handle = fopen($tmp_name, "w"); if (@ftp_fget($this->getConnection(), $handle, $path, FTP_BINARY)) { $handle2 = fopen($tmp_name, r); header("Content-type: " . mime_content_type($tmp_name)); header('Content-Description: File Transfer'); header('Content-Disposition: attachment; filename=' . str_replace(' ', '_', basename($path))); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($tmp_name)); ob_clean(); flush(); fseek($handle2, 0); echo fread($handle2, filesize($tmp_name)); fclose($handle2); unlink($tmp_name); exit; } else { throw new ilCloudException(ilCloudException::DOWNLOAD_FAILED, $rel_path); } } |
This is again quite similar to the method already showed. Explanation about php file-downloading specific functions are omitted at this point.
At last the most important method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /** * @param ilCloudFileTree $file_tree * @param string $rel_parent_folder * @throws ilCloudException */ public function addToFileTree(ilCloudFileTree &$file_tree, $rel_parent_folder = "/") { $parent_folder = ilCloudUtil::joinPathsAbsolute($file_tree->getRootPath(), $rel_parent_folder); if (@ftp_chdir($this->getConnection(), $parent_folder)) { if (is_array($children = @ftp_rawlist($this->getConnection(), ""))) { $items = array(); foreach ($children as $child) { $chunks = preg_split("/\s+/", $child); list($item['rights'], $item['number'], $item['user'], $item['group'], $item['size'], $item['month'], $item['day'], $item['time']) = $chunks; $item['is_dir'] = $chunks[0]{0} === 'd'; array_splice($chunks, 0, 8); $item["name"] = implode(" ", $chunks); $items[] = $item; } foreach ($items as $item) { if ($item["name"] != null && $item["name"] != "." && $item["name"] != ".." && $item['user'] == $this->user_name) { $id = "id_" . sha1($rel_parent_folder . $item["name"]); $path = ilCloudUtil::joinPaths($rel_parent_folder, $item["name"]); $node = $file_tree->addNode($path, $id, $item["is_dir"], strtotime($item['month'] . $item['day'] . $item['time']), $this->formatBytes($item['size'])); $node->setMixed(array("perm" => $item['rights'])); } } $file_tree->setLoadingOfFolderComplete($rel_parent_folder); } } else { throw new ilCloudException(ilCloudException::PERMISSION_DENIED, $parent_folder); } } |
“ftp_chdir” returns a string which contains all folders and files of the specified directory. In the next step the content of this string is put into the array “items” with a few string manipulation methods. After that, the content of all items is added to the file tree with the method “addNode”, which takes the path, a unique id, “is_dir” (bool), a time value and the size as string as parameter. “addNode” returns the node just added to “file_tree”. If needed, additional parameters of the node which is set into “file_tree” can be added with the “setMixed” method of the node. At last, we tell the “file_tree” that the now of “rel_parent_folder” is now completely loaded into “file_tree”.
At this point, the basic setup of the class is complete and if a cloud object with the “ExampleCloud” plugin is created, one can browse through the specified ftp folder inside ILIAS. Note, that so far less than 200 lines of code had to be written so far.
An other method which can be overridden is “getRootId”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * @param $root_path * @return string * @throws ilCloudException */ public function getRootId($root_path) { if (@ftp_chdir($this->getConnection(), $root_path)) { return "id_" . sha1($root_path); } else { throw new ilCloudException(ilCloudException::FOLDER_NOT_EXISTING_ON_SERVICE, $root_path); } } |
This method is called from the core-module whenever to base directory is changed by the user. The “root_id” is stored into the database to get a faster access on files on remote file systems where the access of files with the path is not possible (e.g. GoogleDrive). It is a good practice to check wheter the specified base directory is accessible before setting the “root_id”. If it is not accessible it probably does not exist and user sees the feedback immediately.
Further there are two methods which do not need to be touched when implementing a FTP Connection. In the case of a connection via Dropbox or GoogleDrive API the user will be redirected the a remote authentification site and later redirected back to a callback url. This is done directly after the object is created. There are two methods to implement for those cases (the implementation shows a solution for connecting to a Dropbox folder):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /** * @param string $callback_url */ public function authService($callback_url) { $web_auth = new \Dropbox\WebAuth($this->getConfig()); $callback_url = $this->getProtokol().'://' . $_SERVER['HTTP_HOST'] . '/' . htmlspecialchars_decode($callback_url); list($request_token, $auth_url) = $web_auth->start($callback_url); $_SESSION['cld_cldh_cdpx_request_token'] = $request_token->serialize(); header("Location: " . $auth_url); } public function afterAuthService() { $key = $_GET['oauth_token']; $request_token = \Dropbox\RequestToken::deserialize($_SESSION['cld_cldh_cdpx_request_token']); if ($_GET["not_approved"] || !$request_token->matchesKey($key)) { return false; } $web_auth = new \Dropbox\WebAuth($this->getConfig()); list($accessToken, $dropboxUserId) = $web_auth->finish($request_token); $this->getPluginObject()->setToken($accessToken->serialize()); $this->getPluginObject()->doUpdate(); return true; } |
In “authService”, the user is redirected to the Dropbox website to do the authentication. The “callback_url” sends her or him after the authentication to “afterAuthService” where the token and other informations needed can be stored into the Database of the plugin. Note the use of “$this->getPluginObject()” which grants access to the class representing the model of the plugin. The class of the model is explained in the next section.
Model Class
Most of the time plugins need to write informations (like tokens, usernames, etc.) into the database. Up to now, the standard model class, “ilCloudPlugin”, from the core-module was used. This class does basically nothing. The main purpose of this class is to be extended. An example of how this could be done for the FTP-Plugin created in the last section (again the naming convention for the file, “class. ilYourPluginName.php” or “ilCloudExample.php” in this case, has to be kept):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | <?php include_once("./Modules/Cloud/classes/class.ilCloudPlugin.php"); /** * * @author Timon Amstutz timon.amstutz@ilub.unibe.ch * @version $Id$ * */ class ilExampleCloud extends ilCloudPlugin { /** * @var string */ protected $address = ""; /** * @var string */ protected $user_name = ""; /** * @var string */ protected $password = ""; /** * @var string */ protected $max_file_size = 100; const SALT = "Ich bin ein Geheimer Salt Wert! Entwickler: Bitte Ändere mich! Halunke: Bitte ignoriere mich!"; /** * @param string $address */ public function setAddress($address) { $this->address = $address; } /** * @return string */ public function getAddress() { return $this->address; } /** * @param string $password */ public function setPassword($password) { $this->password = $password; } /** * @return string */ public function getPassword() { return $this->password; } function encrypt($text) { return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, substr($this::SALT,0,32), $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); } function decrypt($text) { return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, substr($this::SALT, 0, 32), base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))); } /** * @param string $user_name */ public function setUserName($user_name) { $this->user_name = $user_name; } /** * @return string */ public function getUserName() { return $this->user_name; } /** * Create object */ function create() { global $ilDB; $ilDB->manipulate("INSERT INTO " . $this->getTableName() . " (id, address, user_name, password) VALUES (" . $ilDB->quote($this->getObjId(), "integer") . " ," . $ilDB->quote($this->getAddress(), "text") . " ," . $ilDB->quote($this->getUserName(), "text") . " ," . $ilDB->quote($this->getPassword(), "text") . " )"); } /** * Read data from db */ function read() { global $ilDB; $set = $ilDB->query("SELECT * FROM " . $this->getTableName() . " WHERE id = " . $ilDB->quote($this->getObjId(), "integer")); $rec = $ilDB->fetchAssoc($set); if ($rec == null) { return false; } else { $this->setAddress($rec["address"]); $this->setUserName($rec["user_name"]); $this->setPassword($this->decrypt($rec["password"])); } return true; } /** * Update data */ function doUpdate() { global $ilDB; $ilDB->manipulate("UPDATE " . $this->getTableName() . " SET id = " . $ilDB->quote($this->getObjId(), "integer") . "," . " address = " . $ilDB->quote($this->getAddress(), "text") . "," . " user_name = " . $ilDB->quote($this->getUserName(), "text") . "," . " password = " . $ilDB->quote($this->encrypt($this->getPassword()), "text") . " WHERE id = " . $ilDB->quote($this->getObjId(), "integer") ); } /** * Delete */ function doDelete() { global $ilDB; $ilDB->manipulate(" DELETE FROM cld_cldh_cdpx_props WHERE " ." id = " . $ilDB->quote($this->getObjId(), "integer")); } } ?> |
|
With this model we can store the values for the server address, the username and the password into the database.
GUI Classes
The model class is not of much use, if the user does not have the possibility to enter values. For this purpose all GUI classes oft he core-module can be extended. A lot of them offer methods, which are empty and meant to overridden. At this point it might be useful to have a closer look at how the classes are organized in the core-module:
All GUI classes are derived from “ilCloudPluginGUI”, which is abstract. All those classes can be extended if necessary. If they are extended, the extended version will be used instead of the original one. “ilCloudPluginGUI” has a reference to “ilCloudPluginService” (or the extended version of it, if one exists) stored. This instance is created once at the very beginning of the execution of the module. “ilCloudPluginService” has a reference to the model “ilCloudPlugin” (or the extended version of it) which then again has a reference to the extended version of “ilCloudHookPlugin” and “ilCloudPluginConfig” model (which could be used to add values which could be configured through the administration). In short: In a GUI class one has accessed to all values from the service and the models.
As an example an extended version of the “ilCloudPluginService” class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <?php include_once("./Modules/Cloud/classes/class.ilCloudPluginSettingsGUI.php"); /** * * @author Timon Amstutz timon.amstutz@ilub.unibe.ch * @version $Id$ * * @ilCtrl_isCalledBy ilExampleCloudSettingsGUI: ilObjCloudGUI */ class ilExampleCloudSettingsGUI extends ilCloudPluginSettingsGUI { public function initPluginSettings() { global $lng; $address = new ilTextInputGUI($this->txt("address"), "address"); $address->setInfo($this->txt("info_address")); $this->form->addItem($address); $name = new ilTextInputGUI($this->txt("user_name"), "user_name"); $name->setInfo($this->txt("info_user_name")); $this->form->addItem($name); $password = new ilPasswordInputGUI($this->txt("password"), "password"); $password->setSkipSyntaxCheck(true); $password->setInfo($this->txt("info_password")); $this->form->addItem($password); } public function updatePluginSettings() { $this->getPluginObject()->setAddress($this->form->getInput("address")); $this->getPluginObject()->setUserName($this->form->getInput("user_name")); $this->getPluginObject()->setPassword($this->form->getInput("password")); $this->getPluginObject()->doUpdate(); } public function getPluginSettingsValues(&$values) { $values["address"] = $this->getPluginObject()->getAddress(); $values["user_name"] = $this->getPluginObject()->getUserName(); $values["password"] = $this->getPluginObject()->getPassword(); } } ?> |
Several things should be noted:
- Language variables can be accessed from GUI classes via “$this->txt”
- The model can be accessed by calling “$this->getPluginObject()”.
- The “@ilCtrl_isCalledBy ilFTPConnectionSettingsGUI: ilObjCloudGUI” line in the commands section is necessary so that ilCtrl finds the class when needed.
- $this->form is instantiated in the parent class, so all there is to do with it, is to add the items needed.
- The language file of the plugin needs to be updated:
1 2 3 4 5 6 7 8 9 10 | ExampleCloud#:#Example Cloud create_info#:#Some Information which is displayed to the user when creating a new Cloud-Object FTPConnection#:#FTP Connection create_info#:#Connection to a Folder on the FTP-Server password#:#Password user_name#:#Username address#:#Address info_password#:#Password for the user on the FTP-Server info_user_name#:#Username on the FTP-Server info_address#:#Address of the FTP-Server (without ftp://) |
- Now the service class method “getConnection” needs to be adapted to take the values of the model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | if (!$this->connection) { $url = $this->getPluginObject()->getAddress(); $user_name = $this->getPluginObject()->getUserName(); $password = $this->getPluginObject()->getPassword(); $this->connection = @ftp_connect(htmlspecialchars(htmlspecialchars_decode($url))); if (!$this->connection) { throw new ilCloudException(ilCloudException::PERMISSION_DENIED, "unknown address: " . $url); } $login_result = ftp_login($this->connection, $user_name, $password); if (!$login_result) { throw new ilCloudException(ilCloudException::AUTHENTIFICATION_FAILED); } } return $this->connection; } |
- One line in the method “addToFileTree” should also be changed:
1 | if ($item["name"] != null && $item["name"] != "." && $item["name"] != ".." && $item['user'] == $this->getPluginObject()->getUserName()) |
- At last there is a dbupdate step to add in the dbupdate.php file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <#1> <?php include_once("./Customizing/global/plugins/Modules/Cloud/CloudHook/FTPConnection/classes/class.ilFTPConnectionPlugin.php"); $plugin_hook_object = new ilExampleCloudPlugin(); $fields = array( 'id' => array( 'type' => 'integer', 'length' => 8, 'notnull' => true ), 'address' => array( 'type' => 'text', 'length' => 256 ), 'user_name' => array( 'type' => 'text', 'length' => 256 ), 'password' => array( 'type' => 'text', 'length' => 256 ), ); $ilDB->createTable($plugin_hook_object->getPluginTableName(), $fields); $ilDB->addPrimaryKey($plugin_hook_object->getPluginTableName(), array("id")); ?> |
That’s it. The FTP-Connection Plugin aka “ExamplePlugin” is now fully functional. It’s not thought for productive purposes (there are things left to be done, like deleting non-empty folders and properly managing non-utf8 chars given from the FTP-Server).
An example of what can be achieved by extending the GUI classes is shown by the Google Drive-Plugin:
Config Classes
Sometimes it is necessary to let administrators store certain values for the plugin in the plugin administration. For the Cloud-Plugin there is a very easy way to do this as shown in the following example of the Dropbox-Plugin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?php include_once("./Modules/Cloud/classes/class.ilCloudPluginConfigGUI.php"); /** * Cloud configuration user interface class * * @author Timon Amstutz <timon.amstutz@ilub.unibe.ch> * @version $Id$ */ class ilDropboxConfigGUI extends ilCloudPluginConfigGUI { /** * @return array */ public function getFields() { return array( "app_name" => array("type" => "ilTextInputGUI", "info" => "config_info_app_name", "subelements" => null), "app_key" => array("type" => "ilTextInputGUI", "info" => "config_info_app_key", "subelements" => null), "app_secret" => array("type" => "ilTextInputGUI", "info" => "config_info_app_secret", "subelements" => null), "conf_max_file_size" => array("type" => "ilCheckboxInputGUI", "info" => "config_info_conf_max_upload_size", "subelements" => null), "default_max_file_size" => array("type" => "ilNumberInputGUI", "info" => "config_info_default_max_upload_size", "subelements" => null), "conf_allow_public_links" => array("type" => "ilCheckboxInputGUI", "info" => "config_info_conf_allow_public_links", "subelements" => null), "default_allow_public_links" => array("type" => "ilCheckboxInputGUI", "info" => "config_default_conf_allow_public_links", "subelements" => null), ); } } ?> |
All there is to do is to define the name of the row in the database (e.g. “app_name”) the input type, the language variable of the info to be shown and possible subelements of this GUI element. The core-classes take care of the rest.
To input some standard values, the dbupdate.php can be used as the following example shows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <#2> <?php include_once("./Modules/Cloud/classes/class.ilCloudPluginConfig.php"); $plugin_object = new ilDropboxPlugin(); $config_object = new ilCloudPluginConfig($plugin_object->getPluginConfigTableName()); $config_object->initDB(); $config_object->setAppName("Custom App Name"); $config_object->setAppKey("Custom App Key"); $config_object->setAppSecret("Custom App Secret"); $config_object->setConfAllowPublicLinks(false); $config_object->setDefaultAllowPublicLinks(true); $config_object->setConfMaxFileSize(30); $config_object->setDefaultMaxFileSize(true); ?> |
In the other classes, the values from the administration model can be accessed like that:
1 2 3 4 5 6 7 8 | if($this->getAdminConfigObject()->getConfMaxFileSize()) { $max_file_size = new ilNumberInputGUI($this->txt("max_file_size"), "max_file_size"); $max_file_size->setInfo($this->txt("info_max_file_size")); $max_file_size->setMaxLength(10); $max_file_size->setSize(10); $this->form->addItem($max_file_size); } |
Ajax
Finally it is very important to know, that the whole content of the plugin is managed by the client. This means that there are no page reloads when navigating through the folders. Even while uploading or creating new folders, there are no page reloads. This makes navigating to previously visited folders very fast. On the downside, implementing new forms and other stuff that is not by default accessed by the ajax request gets a little more complicated. One has to extend the ilCloudFileList javascript function with prototypes. An example of how that is done can be found in the code of the google drive plugin.
Examples
There are three plugins currently under development by the University of Berne (Google Drive, Dropbox and FTP). Due to the fact that they are not ready for publishing, please write an email to timon.amstutz@ilub.unibe.ch to get the source code as an example for own developments.