| Author: | Oliver Geisen |
| Overview: | Create homepages for dokuwiki-users without the need of complex ACLs |
| Based on: | dokuwiki-2006-03-09 |
| Plugins needed: | none |
Please note: This TIP is a simple hack. It would even be better to think all over and find a better solution to provide homepages for users, especially the way to authorize the namespaces they are in. Maybe a variable like %USERID% could be used in ACL.
We (dokuwiki admins) want to give our dokuwiki-users the ability to create their own homepages. These should be in their own responsibility and therefore in their own namespace. The user may create as many pages as he want inside this namespace (Think twice: “should'nt there be some kind of quota ?”). Each user created with the usermanager should be provided with a namespace dedicated to him and a simple starting template. Also there should be a “My page” button in the navigation bar, so users can easily access their pages.
Each page the user see's is requested from the browser like:
http://<server>/doku.php?id=<wiki-path>
doku.php itself will initialize some environment, sets global variables, checks ACLs and will then call main.php from the currently selected template, which is found at:
lib/tpl/<template>/main.php
So we must decide where to hack the code: Inside ACL checking or where page is shown. I decided to do all the work needed just before the page is shown because I want to keep ACLs clean and simple. Therefore we must make some decicions now:
I decide to:
home: (just like unix do)home:* @ALL 0 home:* @user 16
to conf/acl.auth.php.
The next challenge was to find the place in code where user should be given full access to his namespace. We've discovered that main.php is called at least, so let's look inside and we found a call like:
<?php tpl_content()?>
Let's do a simple grep-search inside the dokuwiki base-directory to find the lib which exports this function:
grep -r "function tpl_content" *
The answer is: inc/template.php. So lets look inside the function and determine how it works. It imports some globals, where $ID contains the wiki-url to the page requested and $ACT the thing to do (mostly “show”).
So this leads us to “html_show()”, which we can find, using grep-search like before, inside “inc/html.php”. Further look show that it simply displays/renders the wiki-source-text, so this is too deep to implement the code needed. I decide to put in into “tpl_content()” right after the global definitions:
... global imports ... // if $ID (requested page) starts with 'home:' and we are not the "admin" do some extra checking if((substr($ID,0,5) == 'home:') && ($INFO['perm'] != AUTH_ADMIN)){ $userid = $_SESSION[$conf['title']]['auth']['user']; $allowed = 'home:'.$userid.':'; // if user requests the page of another users homedir, deny access to it if(substr($ID,0,strlen($allowed)) != $allowed){ print p_locale_xhtml('denied'); return; } } // only admins, users requesting pages from their own homespace, or users requesting other namespaces will get here ... regular code continues ...
Add the code to display the new button inside main.php of the template:
<?php tpl_button('mypage') ?>
Now let's find out where tpl_button comes from. Just use grep-search again and see it's inside inc/template.php (haha, surprise surpise, who had thought that all functions prefixed with “tpl_” comes from template.php
))
Ok, i just added a new case-statement for my button:
... case 'mypage': $uid = $_SESSION[$conf['title']]['auth']['user']; $id = 'home:'.$uid.':index'; print html_btn('mypage',$id,'u',array("do" => "show")); break; ...
Next, we need to add the button-label for “mypage”. A quick look inside “html_btn” (which is found in inc/html.php) shows that each label is prefixed with “btn_”, which results in “btn_mypage” in our case, and is looked up in the language-file. This is found at:
inc/lang/<language>/lang.php
Here i append a new line containing:
$lang['btn_mypage'] = 'Meine Seite';
Please note “Mein Seite” is German. Just put whatever YOU want, of course
Now you can try it, and find a good place for the button inside main.php.
Because users are not able to create own namespace in “home:” we must provide them one. Best place to do this is when adding the user with the usermanager. The usermanager is a default plugin of dokuwiki and it's code is found at:
lib/plugins/usermanager/
Next we must find the place where the new user is created to add our code to create the homespace as well. In the “handle()” function we found a case-switch with a case “add” : $this→_addUser(); break; - this is our man!
So lets follow the call to the function “_addUser()”. It first gets data from the POSTed form, which normally contains the params of the new user to be created (name, mail, password, etc.). Next it wraps to $this→_auth→createUser($user,$pass,$name,$mail,$grps);. This function is not contained here, because “_auth” is another object which will be dynamically set by the choosen authentication method.
I use “plaintext” authentication so i found out that inc/auth/plaintext.php is the place to search for “createUser”. All this function do is to add the credentials given to the “user database” and returns “false” if the user already exists, “null” when an error has occured and “true” if everything went well.
Because our solution should not depend upon the selected authentication method, it's best to add it to the usermanager code. The problem here is how to build up the path to the namespace for the user to create with mkdir (or better io_mkdir_p() from inc/io.php).
So lets start thinking reverse again. What happens if one clicks on “Create page”? Yes, a page opens with an empty editfield and a “Save” button under it. This buttons is worth a closer look, so check HTML-source and found out that it will call doku.php with “do=save” and “id=<pageid>”. Now i go all the way through doku.php down until inc/actions.php where “save” is handled and came up to the function “act_save($ACT)”. After a few checks it will call “saveWikiText(…)” which i found in inc/common.php.
Here i found two very interesting things:
For now “io_saveFile()” seems of interest and i found it in inc/io.php. There i found how it's done. The call to
io_makeFileDir($file);
creates the needed directory structure upon the given filepath. So this is the function to use in the usermanager plugin.
Now all there is to do is just create the path to the wanted namespace-id, which is in our case “home:<userid>:index”, and create the directory.
One note about the wikiFN-function. If the last part of the argument given is ':', it results in a path where the element just before the ':' is suffixed with '.txt'. I think this is an error, but maybe not… just keep this in mind ! For heavens sake “io_makeFileDir()” expects also a file as last argument, so there is no problem at all.
Now it's time to fill our code into the appropriate functions of /lib/plugins/usermanager/admin.php:
/* * Create homedir (namespace) for user */ function _createHomedir($user, $name){ $userhome = dirname(wikiFN("home:$user:index")); $homebase = dirname($userhome); // create directory if(! @is_dir($userhome)) { io_makeFileDir($userhome.'/dummy.txt'); msg("Homedir for user created", 1); } // copy template from parent (modify this to fit YOUR needs !) $userpage = $userhome.'/index.txt'; if(! @is_file($userpage)){ $tpl = $homebase.'/template.txt'; if(@is_file($tpl)) { $page = io_readFile($tpl); $page = preg_replace('/@USERNAME@/', $name, $page); io_saveFile($userpage, $page); msg("Template copied to homedir", 1); } } }
if (empty($newuser)) return false; # create homedir for new/existing user $this->_createHomedir($newuser,$newname); $changes = array();
if (empty($user)) return false; # create homedir for new/existing user $this->_createHomedir($user,$name); return $this->_auth->createUser($user,$pass,$name,$mail,$grps);
Ok, thats it! Please read everything carefully and be aware that if you change the sourcecode of dokuwiki, you are unable to upgrade easily.