InDefero

Sign in or create your account | Project List | Help

InDefero Git Source Tree

Root/src/IDF/Plugin/SyncSvn.php

1<?php
2/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3/*
4# ***** BEGIN LICENSE BLOCK *****
5# This file is part of InDefero, an open source project management application.
6# Copyright (C) 2008 Céondo Ltd and contributors.
7#
8# InDefero is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# InDefero is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21#
22# ***** END LICENSE BLOCK ***** */
23
24
25/**
26 * This classes is a plugin which allows to synchronise access rights
27 * between indefero and a DAV powered Subversion repository.
28 */
29class IDF_Plugin_SyncSvn
30{
31    
32    /**
33     * Entry point of the plugin.
34     */
35    static public function entry($signal, &$params)
36    {
37        // First check for the 3 mandatory config variables.
38        if (!Pluf::f('idf_plugin_syncsvn_authz_file', false) or
39            !Pluf::f('idf_plugin_syncsvn_passwd_file', false) or
40            !Pluf::f('idf_plugin_syncsvn_svn_path', false)) {
41            return;
42        }
43        include_once 'File/Passwd/Authdigest.php'; // $ pear install File_Passwd
44        $plug = new IDF_Plugin_SyncSvn();
45        switch ($signal) {
46        case 'IDF_Project::created':
47            $plug->processSvnCreate($params['project']);
48            break;
49        case 'IDF_Project::membershipsUpdated':
50            $plug->processSyncAuthz($params['project']);
51            break;
52        case 'Pluf_User::passwordUpdated':
53            $plug->processSyncPasswd($params['user']);
54            break;
55        case 'IDF_Project::preDelete':
56            $plug->processSvnDelete($params['project']);
57            break;
58        case 'svnpostcommit.php::run':
59            $plug->processSvnUpdateTimeline($params);
60            break;
61        }
62    }
63
64    /**
65     * Run svnadmin command to create the corresponding Subversion
66     * repository.
67     *
68     * @param IDF_Project
69     * @return bool Success
70     */
71    function processSvnCreate($project)
72    {
73        if ($project->getConf()->getVal('scm') != 'svn') {
74            return false;
75        }
76        $shortname = $project->shortname;
77        if (false===($svn_path=Pluf::f('idf_plugin_syncsvn_svn_path',false))) {
78            throw new Pluf_Exception_SettingError("'idf_plugin_syncsvn_svn_path' must be defined in your configuration file.");
79        }
80        if (file_exists($svn_path.'/'.$shortname)) {
81            throw new Exception(sprintf(__('The repository %s already exists.'),
82                                        $svn_path.'/'.$shortname));
83        }
84        $return = 0;
85        $output = array();
86        $cmd = sprintf(Pluf::f('svnadmin_path', 'svnadmin').' create %s',
87                       escapeshellarg($svn_path.'/'.$shortname));
88        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
89        $ll = exec($cmd, $output, $return);
90        if ($return != 0) {
91            Pluf_Log::error(array('IDF_Plugin_SyncSvn::processSvnCreate',
92                                  'Error',
93                                  array('path' => $svn_path.'/'.$shortname,
94                                        'output' => $output)));
95            return;
96        }
97        $p = realpath(dirname(__FILE__).'/../../../scripts/svn-post-commit');
98        exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
99                     escapeshellarg($p),
100                     escapeshellarg($svn_path.'/'.$shortname.'/hooks/post-commit')),
101             $out, $res);
102        if ($res != 0) {
103            Pluf_Log::warn(array('IDF_Plugin_SyncSvn::processSvnCreate',
104                                 'post-commit hook creation error.',
105                                 $svn_path.'/'.$shortname.'/hooks/post-commit'));
106            return;
107        }
108        $p = realpath(dirname(__FILE__).'/../../../scripts/svn-post-revprop-change');
109        exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
110                     escapeshellarg($p),
111                     escapeshellarg($svn_path.'/'.$shortname.'/hooks/post-revprop-change')),
112             $out, $res);
113        if ($res != 0) {
114            Pluf_Log::warn(array('IDF_Plugin_SyncSvn::processSvnCreate',
115                                 'post-revprop-change hook creation error.',
116                                 $svn_path.'/'.$shortname.'/hooks/post-revprop-change'));
117            return;
118        }
119
120        return ($return == 0);
121    }
122
123    /**
124     * Remove the project from the drive and update the access rights.
125     *
126     * @param IDF_Project
127     * @return bool Success
128     */
129    function processSvnDelete($project)
130    {
131        if (!Pluf::f('idf_plugin_syncsvn_remove_orphans', false)) {
132            return;
133        }
134        if ($project->getConf()->getVal('scm') != 'svn') {
135            return false;
136        }
137        $this->SyncAccess($project); // exclude $project
138        $shortname = $project->shortname;
139        if (false===($svn_path=Pluf::f('idf_plugin_syncsvn_svn_path',false))) {
140            throw new Pluf_Exception_SettingError("'idf_plugin_syncsvn_svn_path' must be defined in your configuration file.");
141        }
142        if (file_exists($svn_path.'/'.$shortname)) {
143            $cmd = Pluf::f('idf_exec_cmd_prefix', '').'rm -rf '.$svn_path.'/'.$shortname;
144            exec($cmd);
145        }
146    }
147    
148    /**
149     * Synchronise an user's password.
150     *
151     * @param Pluf_User
152     */
153    function processSyncPasswd($user)
154    {
155        $passwd_file = Pluf::f('idf_plugin_syncsvn_passwd_file');
156        if (!file_exists($passwd_file) or !is_writable($passwd_file)) {
157            return false;
158        }
159        $ht = new File_Passwd_Authbasic($passwd_file);
160        $ht->load();
161        $ht->setMode(FILE_PASSWD_SHA);
162        if ($ht->userExists($user->login)) {
163            $ht->changePasswd($user->login, $this->getSvnPass($user));
164        } else {
165            $ht->addUser($user->login, $this->getSvnPass($user));
166        }
167        $ht->save();
168        return true;
169    }
170
171    /**
172     * Synchronize the authz file and the passwd file for the project.
173     *
174     * @param IDF_Project
175     */
176    function processSyncAuthz($project)
177    {
178        $this->SyncAccess();
179        $this->generateProjectPasswd($project);
180    }
181
182    /**
183     * Get the repository password for the user
184     */
185    function getSvnPass($user){
186        return substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
187    }
188
189    /**
190     * For a particular project: update all passwd information
191     */
192    function generateProjectPasswd($project)
193    {
194        $passwd_file = Pluf::f('idf_plugin_syncsvn_passwd_file');
195        if (!file_exists($passwd_file) or !is_writable($passwd_file)) {
196            return false;
197        }
198        $ht = new File_Passwd_Authbasic($passwd_file);
199        $ht->setMode(FILE_PASSWD_SHA);
200        $ht->load();
201        $mem = $project->getMembershipData();
202        $members = array_merge((array)$mem['members'], (array)$mem['owners'],
203                               (array)$mem['authorized']);
204        foreach($members as $user) {
205            if ($ht->userExists($user->login)) {
206                $ht->changePasswd($user->login, $this->getSvnPass($user));
207            } else {
208                $ht->addUser($user->login, $this->getSvnPass($user));
209            }
210        }
211        $ht->save();
212    }
213
214    /**
215     * Generate the dav_svn.authz file
216     *
217     * We rebuild the complete file each time. This is just to be sure
218     * not to bork the rights when trying to just edit part of the
219     * file.
220     *
221     * @param IDF_Project Possibly exclude a project (null)
222     */
223    function SyncAccess($exclude=null)
224    {
225        $authz_file = Pluf::f('idf_plugin_syncsvn_authz_file');
226        $access_owners = Pluf::f('idf_plugin_syncsvn_access_owners', 'rw');
227        $access_members = Pluf::f('idf_plugin_syncsvn_access_members', 'rw');
228        $access_extra = Pluf::f('idf_plugin_syncsvn_access_extra', 'r');
229        $access_public = Pluf::f('idf_plugin_syncsvn_access_public', 'r');
230        $access_public_priv = Pluf::f('idf_plugin_syncsvn_access_private', '');
231        if (!file_exists($authz_file) or !is_writable($authz_file)) {
232            return false;
233        }
234        $fcontent = '';
235        foreach (Pluf::factory('IDF_Project')->getList() as $project) {
236            if ($exclude and $exclude->id == $project->id) {
237                continue;
238            }
239            $conf = new IDF_Conf();
240            $conf->setProject($project);
241            if ($conf->getVal('scm') != 'svn' or
242                strlen($conf->getVal('svn_remote_url')) > 0) {
243                continue;
244            }
245            $mem = $project->getMembershipData();
246            // [shortname:/]
247            $fcontent .= '['.$project->shortname.':/]'."\n";
248            foreach ($mem['owners'] as $v) {
249                $fcontent .= $v->login.' = '.$access_owners."\n";
250            }
251            foreach ($mem['members'] as $v) {
252                $fcontent .= $v->login.' = '.$access_members."\n";
253            }
254            // access for all users
255            if ($project->private == true) {
256                foreach ($mem['authorized'] as $v) {
257                    $fcontent .= $v->login.' = '.$access_extra."\n";
258                }
259                $fcontent .= '* = '.$access_public_priv."\n";
260            } else {
261                $fcontent .= '* = '.$access_public."\n";
262            }
263            $fcontent .= "\n";
264        }
265        file_put_contents($authz_file, $fcontent, LOCK_EX);
266        return true;
267    }
268
269    /**
270     * Update the timeline in post commit.
271     *
272     */
273    public function processSvnUpdateTimeline($params)
274    {
275        $pname = basename($params['repo_dir']);
276        try {
277            $project = IDF_Project::getOr404($pname);
278        } catch (Pluf_HTTP_Error404 $e) {
279            Pluf_Log::event(array('IDF_Plugin_SyncSvn::processSvnUpdateTimeline', 'Project not found.', array($pname, $params)));
280            return false; // Project not found
281        }
282        // Now we have the project and can update the timeline
283        Pluf_Log::debug(array('IDF_Plugin_SyncGit::processSvnUpdateTimeline', 'Project found', $pname, $project->id));
284        IDF_Scm::syncTimeline($project, true);
285        Pluf_Log::event(array('IDF_Plugin_SyncGit::processSvnUpdateTimeline', 'sync', array($pname, $project->id)));
286        
287
288    }
289}
290

Archive Download this file

Branches:
dev
develop
master
newdiff
svn

Tags:
v1.0