# $DEBUG = 2 ;
$SEE_ALSO = 'see also' ;
$EDS = array () ;
function is_adm ()
{ global $USER, $EDS ;
if ( count ( $EDS ) == 0 )
{ $roots = get_repo_roots ( '', '', '' ) ;
foreach ( $roots as $root )
{ $EDS [ $root -> pid ] = ist ( $root -> actv ) ; }
}
return ( $USER and $EDS [ $USER ] ) ;
}
function isroot ()
{ global $USER, $EDS ;
$sink = is_adm () ;
return ( $USER and array_key_exists ( $USER, $EDS ) ) ;
}
function assert_isroot ()
{ global $USER ;
if ( ! isroot () ) { html_exit ( "you aren't an admin ($USER)" ) ; }
}
function switch_url ()
{ $adm = is_adm () ? 'OFF' : 'ON' ;
$txt = ITA ( is_adm () ? 'inactive' : 'active' ) ;
$uri = $_SERVER [ "REQUEST_URI" ] ;
$uri .= ( preg_match ( '/\?/', $uri ) ? '&' : '?' ) . "ADM=$adm" ;
return URL ( $uri, $txt ) ;
}
function login_url ()
{ $uri = $_SERVER [ 'REQUEST_URI' ] ;
if ( ! preg_match ( '/login=1/', $uri ) )
{ $uri .= ( preg_match ( '/\?/', $uri ) ? '&' : '?' ) . 'login=1' ; }
# echo $uri ;
if ( preg_match ( '/[\'"]/', urldecode ( $uri ) ) )
{ $uri = "/?login=1" ; }
return URL ( $uri, 'login' ) ;
}
function logout_url ()
{ return URL ( "index.php?logout=1", 'logout' ) ; }
$GETVAR_pats = array
( 'MAIL' => '/^[^;\'"]*$/'
, 'UID' => '/^[0-9]+$/'
, 'RID' => '/^[0-9]+$/'
, 'XID' => '/^[0-9]+$/'
, 'ID' => '/^[0-9]*$/'
, 'PID' => '/^[0-9]+$/'
, 'AUSR' => '/^([-@.\w]+)?$/i'
, 'AMOD' => '/^ro|rw|nx$/'
, 'ADM' => '/^(OFF|ON)?$/'
, 'PATH' => '/^(\w+(\/\w+)*)?$/'
, 'APTH' => '/^(\w+(\/\w+)*\/?)?$/'
, 'PUBL' => '/^[tf]$/'
, 'UPBL' => '/^[tf]$/'
, 'RIDS' => '/^(\d+(,\d+)*)?$/'
) ;
function GETVAR ( $var )
{ global $GETVAR_pats ;
$res = trim ( $_GET [ $var ] ) or $res = trim ( $_POST [ $var ] ) ;
$txt = htmlentities ( $res ) ;
if ( func_num_args() == 2 )
{ $pat = func_get_arg ( 1 ) ;
if ( $res and ! preg_match ( $pat, $res ) )
{ html_exit ( "$var ($txt) != '$pat'" ) ; }
}
elseif ( $GETVAR_pats [ $var ] )
{ $pat = $GETVAR_pats [ $var ] ;
if ( ! preg_match ( $pat, $res ) )
{ html_exit ( "$var ($txt) != '$pat'" ) ; }
}
return $res ;
}
function CHKVAR ( $name ) # or CHKVAR ( $name, $idx )
{ return
( ( ! array_key_exists ( $name, $_POST )
or ( ( func_num_args() == 2 )
and ( ! array_key_exists ( func_get_arg ( 1 ), $_POST [ $name ] ) )
)
)
? 'f'
: 't'
) ;
}
function REPO_TIT ( $txt )
{ global $DEV_TIT ;
$www = Conf ( 'www_server' ) ;
return "repocafe self-service $www$DEV_TIT - $txt" ;
}
function G_CLASS () { return 'guests' ; }
function G_PREFIX () { return 'g_' ; }
function STAR () { return BLD ( SPN ( '*', 'red' ) ) . ' ' ; }
function ist ( $b ) { return ( is_bool ( $b ) ? $b : $b == 't' ) ; }
function repr_bool ( $b ) { return ist ( $b ) ? 'yes' : 'no' ; }
function dmp ( $m ) { echo sprintf ( "%s
\n", htmlspecialchars ( $m ) ) ; }
function EPS () { return '' ; }
function alpha_date ( $date )
{ $time = time () ;
if ( $date )
{ list ( $y, $m, $d ) = explode ( '-', $date ) ;
$time = mktime ( 12, 12, 12, $m, $d, $y ) ;
}
return date ( 'd F Y', $time ) ;
}
function KMGB ( $s )
{ if ( ! $s ) { return 'unknown' ; }
$x = $s -> size ;
if ( $x > 1024 * 1024 )
{ $res = sprintf ( "%.1f GB", $x / 1024 / 1024 ) ; }
elseif ( $x > 1024 )
{ $res = sprintf ( "%.1f MB", $x / 1024 ) ; }
else
{ $res = sprintf ( "%d KB", $x ) ; }
$date = date ( 'r', $s -> time ) ;
return "± $res on $date" ;
}
function checkbox ( $name, $val )
{ $chk = ist ( $val ) ? 'CHECKED' : '' ;
return "" ;
}
function form_row ( $opt, $th, $td )
{ $S = STAR () ;
if ( func_num_args() == 4 )
{ $spn = func_get_arg ( 3 ) ; $colspan = "COLSPAN=$spn" ; }
return TR
( THA ( "ALIGN=RIGHT", ( $opt == 'req' ? $S : '' ) . $th )
. TDA ( "$colspan CLASS=$opt", $td )
) ;
}
function repo_guests_url ()
{ return URL ( "users.php", G_CLASS () ) ; }
function new_repo_guest_url ()
{ return URL ( "users.php?ACT=NEW_USER_FORM", 'create a new guest' ) ; }
function conf_url ()
{ return URL ( "access.php", 'access' ) ; }
function about_url ()
{ return URL ( "about.php", 'documentation' ) ; }
function stats_url ()
{ return URL ( "stats.php", 'stats' ) ; }
function prefs_url ()
{ $txt = 'preferences' ;
return URL ( "prefs.php?ACT=UPD_PREF_FORM", $txt ) ;
}
function is_guest () { global $CLAS ; return $CLAS == G_CLASS () ; }
function is_login_name ( $name )
{ $res = '' ;
if ( is_a_group_name ( $name ) )
{ ; }
else
{ $user = new User ; $user -> init ( $name ) ;
if ( $user -> find () )
{ $res = $user -> clas ; }
}
return $res ;
}
function is_a_group_name ( $name )
{ return substr ( $name, 0, 1 ) == '@' ; }
function is_group_name ( $name )
{ $res = '' ;
if ( is_a_group_name ( $name ) )
{ $res = try_repo_group_by_name ( $name ) ; }
return $res ;
}
function name_login ( $login, $sep = ' - ' )
{ $res = "$login" ;
if ( ! is_a_group_name ( $login ) )
{ if ( $name = full_name ( $login ) )
{ $res = "$name$sep$res" ; }
else
{ $res = BLD ( SPN ( $res, 'red' ) ) ; }
}
return $res ;
}
function gen_conf_repo ( $repo, $group4name )
{ $res = '' ;
$subs = $repo -> subs ;
foreach ( $subs as $sub )
{ $side = $sub -> side ;
$ownr = $sub -> ownr ;
$name = $sub -> repo_name () ;
$path = $sub -> path ;
$res .= "\n[$name:/$path]\n" ;
$ros = array () ;
$rws = array () ;
$nxs = array () ;
foreach ( $sub -> rights as $rr )
{ $mod = $rr -> mod ;
$pid = $rr -> pid ;
if ( $group4name !== NULL )
{ if ( is_a_group_name ( $pid ) and ! $group4name [ $pid ] )
{ $res .= "# group not found ($pid=$mod)\n" ; }
}
if ( $mod == 'ro' )
{ $ros [ $pid ] ++ ; }
elseif ( $mod == 'rw' )
{ $rws [ $pid ] ++ ; }
elseif ( $mod == 'nx' )
{ $nxs [ $pid ] ++ ; }
# $res .= "# $pid -> $mod\n" ;
}
$k_ros = array_keys ( $ros ) ; sort ( $k_ros ) ;
$k_rws = array_keys ( $rws ) ; sort ( $k_rws ) ;
$k_nxs = array_keys ( $nxs ) ; sort ( $k_nxs ) ;
if ( ! $sub -> rights )
{ $tag = $sub -> tag () ;
$res .= "# gen_conf: no rights for ($tag)\n" ;
}
if ( count ( $k_rws ) )
{ foreach ( $k_rws as $pid )
{ $res .= "$pid = rw\n" ; }
}
if ( $sub -> is_public () )
{ $res .= "# anonymous read allowed\n" . "* = r\n" ; }
elseif ( count ( $k_ros ) )
{ foreach ( $k_ros as $pid ) { $res .= "$pid = r\n" ; } }
if ( ! $sub -> is_public () )
{ $res .= "# anonymous read not allowed\n" . "* =\n" ; }
elseif ( count ( $k_nxs ) )
{ foreach ( $k_nxs as $pid ) { $res .= "$pid =\n" ; } }
}
return $res ;
}
function gen_conf ( $repos, $groups )
{ $res = '' ;
$group4name = array () ;
if ( count ( $groups ) )
{ $res .= "[groups]\n" ;
foreach ( $groups as $group )
{ $name = $group -> name ;
$group4name [ $name ] = $group ;
$mems = array () ;
foreach ( $group -> mems as $mem )
{ if ( ist ( $mem -> mem ) )
{ $mems [ $mem -> pid ] ++ ; }
}
ksort ( $mems ) ;
$res .= sprintf
( "%s = %s\n"
, substr ( $name, 1 )
, implode ( ",", array_keys ( $mems ) )
) ;
}
}
if ( count ( $repos ) )
{ $res .= "\n[/]\n" . "# default : no access\n" . "* =\n" ;
foreach ( $repos as $repo )
{ $res .= gen_conf_repo ( $repo, $group4name ) ; }
}
return $res ;
}
################################################################
function make_guest_list ()
{ $prog = MK_GUEST_LIST () ;
$file = GUEST_LIST () ;
$ADMIN = DIR_ADMIN () ;
$perl = Conf ( 'cmd_perl' ) ;
$line = system ( "cd $ADMIN ; $perl $prog $file", $err ) ;
if ( $err )
{ $res = "cd $ADMIN ; /usr/bin/perl $prog $file : FAIL ($line ($err))" ;
html_exit ( $res ) ;
}
}
function make_svnaccess_file ( $res = 0 )
{ $repos = get_repos ( '', '', 'side,ownr,name' ) ;
$groups = get_repo_groups ( '', '', '' ) ;
$conf = gen_conf ( $repos, $groups ) ;
$file = SVN_ACC_FILE () ;
$temp = "$file.tmp" ;
$err = file_put_contents ( $temp, $conf ) ;
if ( $err === FALSE )
{ html_exit ( "FAIL : file_put_contents $temp" ) ; }
elseif ( ! rename ( $temp, $file ) )
{ html_exit ( "FAIL : rename $temp $file" ) ; }
return $res ? $conf : '' ;
}
function get_repo_guest ( $id )
{ $res = new Repo_guest ;
$res -> db_get_id ( $id ) || html_exit ( "can't get repo_guest ($id)" ) ;
return $res ;
}
function get_repo_guests ( $from, $sql, $order )
{ $res = new Repo_guest ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
function try_repo_guest_by_login ( $login )
{ $res = get_repo_guests ( '', "login = '$login'", 'login' ) ;
if ( count ( $res ) == 1 )
{ return $res [ 0 ] ; }
else
{ return '' ; }
}
function get_repo_guest_by_login ( $login )
{ if ( $user = try_repo_guest_by_login ( $login ) )
{ return $user ; }
else
{ html_exit ( "no repo_guest with login ($login)" ) ; }
}
function repo_guests_info_head ()
{ $may = is_adm () ;
return TR
( ( may_create_guest () ? TH ( 'act' ) : '' )
. TH ( 'login' )
. TH ( 'name' )
. ( $may
? TH ( 'email' ) . TH ( 'created' ) . TH ( 'by' )
: ''
)
) ;
}
class Repo_table extends Table
{ function table_init ( $name, $id )
{ parent::table_init ( Conf ( 'db_pref' ) . $name, $id ) ; }
function _init_object ( $tup, $flds )
{ foreach ( $flds as $attr => $key )
{ $this -> $attr = $tup -> $key ; }
return $this ;
}
static function init_objects ( $typ, $tups, $map )
{ $new = new $typ ;
$flds = $new -> all_fields () ;
foreach ( $flds as $key => $descr )
{ $flds [ $key ] = $key ; }
foreach ( $map as $attr => $key )
{ $flds [ $attr ] = $key ; }
$patt = $new -> _pkey ;
$pkey = $flds [ $patt ] ;
$res = array () ;
$idx = array () ;
$cnt = 0 ;
$res = array () ;
foreach ( $tups as $tup )
{ if ( ! $idx [ $tup -> $pkey ] )
{ $new = new $typ ;
$new -> _init_object ( $tup, $flds ) ;
# echo ++ $cnt ; echo " patt ($patt) pkey ($pkey)
\n" ;
# var_dump ( $new ) ; echo "
\n" ;
$idx [ $new -> $patt ] = $new ;
$res [] = $new ;
}
}
return $res ;
}
static function init_object ( $typ, $tups, $map )
{ $repos = self::init_objects ( $typ, $tups, $map ) ;
return array_shift ( $repos ) ;
}
}
function may_create_guest ()
{ global $CLAS ;
return ( is_adm () or array_key_exists ( $CLAS, Conf ( 'creag' ) ) ) ;
}
class Repo_guest extends Repo_table
{ static $fields_bool = array () ;
function inst () { return new Repo_guest ; }
function Repo_guest () { $this -> table_init ( 'repo_guests', 'id' ) ; }
function next_val () { return parent::_next_val () ; }
function class_name () { return "guest" ; }
function tag ()
{ return sprintf ( '%s (%s)', $this -> login, $this -> name ) ; }
function fields ()
{ return array
( 'login' => 'user name'
, 'name' => 'full name'
, 'email' => 'email address'
, 'parent' => 'created by'
, 'passwd' => 'password'
, 'nwmail' => 'new mail address'
, 'nwcode' => 'update code'
) ;
}
function reget ()
{ $login = $this -> login ;
if ( $this -> id )
{ $res = get_repo_guest ( $this -> id ) ; }
else
{ html_exit ( "can't reget repo_guest ($login) : no id" ) ; }
return $res ;
}
function get_repos ( $order )
{ $pid = $this -> login ;
$Repos = TBL ( 'repos' ) ;
$Rights = TBL ( 'rights' ) ;
$sub = "SELECT rid FROM $Rights WHERE pid = '$pid'" ;
$qwe = "$Repos.id in ( $sub )" ;
return get_repos ( '' , $qwe , $order ) ;
}
function get_repo_groups ( $order )
{ $pid = $this -> login ;
$tab_mems = TBL ( 'repo_group_members' ) ;
$sub = "SELECT gid FROM $tab_mems WHERE pid = '$pid'" ;
$qwe = "gid in ( $sub )" ;
return get_repo_groups ( '' , $qwe , $order ) ;
}
function may_update ()
{ global $USER ;
return ( is_adm () or $this -> parent == $USER ) ;
}
function assert_may_update ()
{ if ( ! $this -> may_update () )
{ html_exit ( "you may not update this guest '{$this->name}'" ) ; }
}
function upd_url ( $txt )
{ return URL ( "users.php?ACT=UPD_USER_FORM&UID={$this->id}", $txt) ; }
function del_url ()
{ $txt = SPN ( 'delete this guest', 'red' ) ;
return URL ( "users.php?ACT=DEL&UID={$this->id}", $txt ) ;
}
function mail_pwd_url ( $txt )
{ return URL ( "users.php?ACT=MAIL&UID={$this->id}", $txt ) ; }
function repr ( $fld )
{ $res = $this -> $fld ;
if ( self::$fields_bool [ $fld ] )
{ $res = repr_bool ( $res ) ; }
return $res ;
}
function info_row ()
{ return TR
( ( may_create_guest ()
? TD
( $this -> may_update ()
? $this -> upd_url ( 'upd' )
: ''
)
: ''
)
. TD ( $this -> login )
. TD ( $this -> name )
. ( is_adm ()
? TD ( $this -> email )
. TD ( $this -> creat )
. TD ( $this -> parent )
: ''
)
) ;
}
function gen_rand ( $n )
{ # abc = [a-zA-Z0-9] - [giloqIOQ019] ; to avo1d typos
$abc = 'abcdefhjkmnprstuvwxyzABCDEFGHJKLMNPRSTUVWXYZ2345678' ;
$res = '' ;
for ( $i = 0 ; $i < $n ; $i ++ )
{ $r = rand ( 0, strlen ( $abc ) - 1 ) ;
$res .= substr ( $abc, $r, 1 ) ;
} ;
return $res ;
}
function set_pw ()
{ $pswd = $this -> gen_rand ( 8 ) ;
$salt = $this -> gen_rand ( 2 ) ;
$this -> passwd = crypt ( $pswd, $salt ) ;
$this -> update_pw () ;
return $pswd ;
}
function set_defaults ()
{ global $USER ;
$this -> login = '' ;
$this -> passwd = 'eeeeeeeeeeeee' ;
$this -> name = '' ;
$this -> email = '' ;
$this -> parent = $USER ;
$this -> nwmail = '' ;
$this -> nwcode = '' ;
}
# for "guest create" and "guest password reset"
function send_mail ( $doit )
{ global $USER ;
$conf = Conf () ;
$serv = $conf -> www_server ;
$schm = $conf -> www_scheme ;
$from = $conf -> mail_from_admin ;
$to = $this -> email ;
$name = $this -> name ;
$login = $this -> login ;
$pswd = $this -> set_pw () ;
$uname = $USER ? full_name ( $USER ) : '' ;
$head = "From: $from\n" ;
$txt = <<
TXT;
if ( ! $doit )
{ $head .= "X-to: $to\n" ;
$to = 'henkp@cs.uu.nl' ;
}
if ( mail ( $to, "$serv registration", $txt, $head ) )
{ $res = "sent passwd to $to" ; }
else
{ $res = "failed: mail to $to" ; }
return $res ;
}
function gen_form_guest ( $act )
{ $login = $this -> login ;
$passwd = $this -> passwd ;
$name = $this -> name ;
$email = $this -> email ;
$PREF = G_PREFIX () ;
if ( $login == '' ) { $login = $PREF ; }
$fields = $this -> fields () ;
$R = STAR () ;
$login_txt = <<{$fields['login']} |
starts with $PREF
|
LOGIN;
if ( $this -> id ) { $login_txt = '' ; }
$res = <<
FORM;
return $res ;
}
function get_form ()
{ $res = '' ;
$_id = trim ( $_POST [ 'ID' ] ) ;
$_login = trim ( $_POST [ 'LOGIN' ] ) ;
$_name = trim ( $_POST [ 'NAME' ] ) ;
$_email = trim ( $_POST [ 'EMAIL' ] ) ;
if ( $this -> id )
{ $_id = $this -> id ;
$_login = $this -> login ;
}
else
{ # we may use 'name' as a base for the login-name
if ( $_login == G_PREFIX () )
{ $_login = G_PREFIX () . $_name ; }
$_login = strtolower ( $_login ) ;
$_login = preg_replace ( '/\s/', '', $_login ) ;
}
$this -> id = $_id ;
$this -> login = $_login ;
$this -> name = $_name ;
$this -> email = $_email ;
$PREF = G_PREFIX () ;
if ( ! may_create_guest () )
{ $err .= "you may not create guests" ; }
if ( ! preg_match ( "/^[a-z]\w+$/", $_login ) )
{ $res .= LI ( "bad characters in user-name ($_login)" ) ; }
if ( ! preg_match ( "/^\w[-\w\s.,()]+$/", $_name ) )
{ $res .= LI ( "bad characters in full name ($_name)" ) ; }
if ( ! preg_match ( "/^\w[-\w.@]+$/", $_email ) )
{ $res .= LI ( "bad characters in email ($_email)" ) ; }
if ( substr ( $_login, 2 ) == '' )
{ $res .= LI ( "user-name must not be empty" ) ; }
if ( $_name == '' )
{ $res .= LI ( "full name must not be empty" ) ; }
if ( $_email == '' )
{ $res .= LI ( "email must not be empty" ) ; }
if ( ! preg_match ( "/^$PREF/", $_login ) )
{ $res .= LI ( "login doesn't begin with '$PREF' ($_login)" ) ; }
if ( ! $_id and try_repo_guest_by_login ( $_login ) )
{ $res .= LI ( "login already exists ($_login)" ) ; }
return ( $res ? UL ( $res ) : '' ) ;
}
function gen_form_prefs ( $act )
{ $res = <<
FORM;
return $res ;
}
function get_form_prefs ()
{ $res = '' ;
$passwd = trim ( $_POST [ 'OLDPWD' ] ) ;
$newpw1 = trim ( $_POST [ 'NEWPW1' ] ) ;
$newpw2 = trim ( $_POST [ 'NEWPW2' ] ) ;
if ( $passwd == '' )
{ $res .= LI ( "password must not be empty" ) ; }
else
{ $salt = $this -> passwd ;
if ( crypt ( $passwd, $salt ) != $this -> passwd )
{ $res .= LI ( "incorrect old password" ) ; }
}
if ( $newpw1 == '' )
{ $res .= LI ( "new password must not be empty" ) ; }
elseif ( $newpw1 != $newpw2 )
{ $res .= LI ( "new passwords not equal" ) ; }
if ( ! $res )
{ $this -> passwd = crypt ( $newpw1, $this -> gen_rand ( 2 ) ) ; }
return ( $res ? UL ( $res ) : '' ) ;
}
function insert ()
{ $this -> id = $this -> next_val () ;
$flds = $this -> fields () ;
$flds [ 'id' ] = 'id' ;
$res = parent::insert ( $flds ) ;
make_guest_list () ;
return $res ;
}
function update ()
{ $res = parent::update () ;
make_guest_list () ;
return ULmk ( $res ? $res : 'no change' ) ;
}
function update_pw ()
{ $res = parent::update ( array ( 'passwd' => 'password' ) ) ;
if ( $res ) { make_guest_list () ; }
return ULmk ( $res ? 'changed password' : 'no change' ) ;
}
function delete ()
{ $pid = $this -> login ;
$lis = '' ;
# delete in repo_rights
$repos = $this -> get_repos ( 'side,name' ) ;
foreach ( $repos as $idx => $repo )
{ if ( $rr = $repo -> rights [ $pid ] )
{ $name = $repo -> repo_name () ;
$lis .= LI ( $name . ' : ' . UL ( $rr -> delete () ) ) ;
}
}
# delete in repo_group_members
$groups = $this -> get_repo_groups ( 'name' ) ;
foreach ( $groups as $group )
{ if ( $mem = $group -> mems [ $pid ] )
{ $name = $group -> name ;
$lis .= LI ( $name . ' : ' . UL ( $mem -> delete () ) ) ;
}
}
# delete in repo_guests
$qwe = sprintf
( 'DELETE FROM %s WHERE id = %s'
, $this -> _tab
, '$1'
) ;
$tab = array ( $this -> id ) ;
db_query_params ( $qwe, $tab ) ;
make_guest_list () ;
$lis .= LI ( "deleted : $pid ({$this->name})" ) ;
return UL ( $lis ) ;
}
}
################################################################
function try_repo ( $id )
{ $Repos = TBL ( 'repos' ) ;
$res = get_repos ( '', "$Repos.id = '$id'", "$Repos.id" ) ;
# $cnt = count ( $res ) ; print "cnt ($cnt)
\n" ;
# var_dump ( $res [ 0 ] ) ;
return ( count ( $res ) == 1 ? $res [ 0 ] : '' ) ;
}
function get_repo ( $id )
{ $res = try_repo ( $id ) ;
if ( ! $res ) { html_exit ( "can't get repo ($id)" ) ; }
return $res ;
}
function try_repo_by_son ( $side, $ownr, $name )
{ $qwe = "side = '$side' and ownr = '$ownr' and name = '$name' " ;
$res = get_repos ( '', $qwe, 'id' ) ;
if ( count ( $res ) == 1 )
{ return $res [ 0 ] ; }
else
{ return '' ; }
}
function get_repos ( $from, $sql, $order )
{ $Repos = TBL ( 'repos' ) ;
$Paths = TBL ( 'paths' ) ;
$Rights = TBL ( 'rights' ) ;
if ( $order )
{ $list = array () ;
foreach ( explode ( ',', $order ) as $fld )
{ $list [] = ( strstr ( $fld, '.' ) ? '' : "$Repos." ) . $fld ; }
$order = implode ( ',', $list ) ;
}
$WHERE = $sql ? "WHERE $sql" : '' ;
$ORDER = $order ? "ORDER BY $order" : '' ;
$qwe = << 'repos_id' ) ) ;
$paths = Repo_table::init_objects
( 'Repo_paths', $tups, array
( 'id' => 'paths_id'
, 'rid' => 'repos_id'
)
) ;
$repo_idx = array () ;
foreach ( $repos as $repo ) { $repo_idx [ $repo -> id ] = $repo ; }
$path_idx = array () ;
# echo count ( $paths ) ; echo " #paths
\n" ;
foreach ( $paths as $Path )
{ $Path_id = $Path -> id ;
$path_idx [ $Path_id ] = $Path ;
$path = $Path -> path ;
$repo_id = $Path -> rid ;
$Repo = $repo_idx [ $repo_id ] ;
$Repo -> paths [ $path ] = $Path ;
$Repo -> prrs [ $path ] = array () ;
$Repo -> pids [ $Path_id ] = $Path ;
$Path -> rrs = array () ;
# var_dump ( $Path ) ; echo "
\n" ;
}
# echo "end paths
\n" ;
$rights = Repo_table::init_objects
( 'Repo_rights', $tups, array ( 'id' => 'rights_id' ) ) ;
# echo count ( $rights ) ; echo " #rights
\n" ;
foreach ( $rights as $rid => $rr )
{ $Path = $path_idx [ $rr -> rid ] ;
$path = $Path -> path ;
$path_id = $Path -> id ;
$repo_id = $Path -> rid ;
$Repo = $repo_idx [ $repo_id ] ;
# echo "path ($path) repo_id ($repo_id) Repo ($Repo)
\n" ;
$Repo -> rrids [ $rr -> id ] = $rr ;
$Repo -> prrs [ $path ] [ $rr -> pid ] = $rr ;
$Path -> rrs [ $rr -> pid ] = $rr ;
}
foreach ( $repos as $repo )
{ ksort ( $repo -> paths ) ;
$repo -> subs = $repo -> make_subs () ;
}
return ( $order ? $repos : $repo_idx ) ;
}
function cnt_repos ( $from, $sql )
{ $sel = new Repo ; return $sel -> db_get_cnt ( $from, $sql ) ; }
function get_repos_xusers ( $order, $pid = '' )
{ $Repos = TBL ( 'repos' ) ;
$Paths = TBL ( 'paths' ) ;
$Rights = TBL ( 'rights' ) ;
$tab_xusers = TBL ( 'repo_xusers' ) ;
$from = "$Paths LEFT JOIN $Rights ON ( $Paths.id = $Rights.rid )" ;
$sub =
( $pid
? " = '$pid' "
: " in ( SELECT pid from $tab_xusers )"
) ;
$sub1 = "SELECT $Paths.rid FROM $from where $Rights.pid $sub" ;
$qwe = "$Repos.id in ( $sub1 )" ;
return get_repos ( '' , $qwe , $order ) ;
}
function repos_url ()
{ return URL ( "index.php", "repositories" ) ; }
function new_repo_url ()
{ return URL ( "index.php?ACT=NEW_REPO_FORM", 'create a new repository' ) ; }
function repos_info_head ()
{ global $USER ;
return TR
( TH ( 'meta' )
. TH ( 'repository' )
. TH ( 'description' )
. ( $USER
? TH ( 'public' )
. TH ( 'created' )
. TH ( 'by' )
: ''
)
) ;
}
function may_create_repo ()
{ global $CLAS ; return count ( paths4clas ( $CLAS ) ) ; }
class Repo extends Repo_table
{ static $fields_bool = array () ;
public $rrids = array () ;
public $prrs = array () ;
public $pids = array () ;
public $subs = array () ;
function inst () { return new Repo ; }
function Repo () { $this -> table_init ( 'repos', 'id' ) ; }
function tag () { return $this -> repo_name () ; }
function class_name () { return 'repo' ; }
function next_val () { return parent::_next_val () ; }
static function fields ( $act )
{ if ( $act == 'new' )
{ $res = array
( 'id' => 'id'
, 'side' => 'side'
, 'ownr' => 'owner'
, 'name' => 'name'
, 'dscr' => 'description'
) ;
if ( is_adm () ) { $res [ 'lock' ] = 'locked' ; }
}
elseif ( $act == 'upd' )
{ $res = array
( 'dscr' => 'description'
) ;
}
else
{ html_exit ( "bad act for fields ($act)" ) ; }
return $res ;
}
static function all_fields ()
{ $res = self::fields ( 'new' ) ;
$res [ 'id' ] = 'id' ;
$res [ 'lock' ] = 'lock' ;
$res [ 'creat' ] = 'creat' ;
return $res ;
}
function cnt_rr_by_group ( $pid, $sql )
{ global $TBL ;
$rid = $this -> id ;
$Groups = TBL ( 'groups' ) ;
$Mems = TBL ( 'gr_mem' ) ;
$Paths = TBL ( 'paths' ) ;
$Rights = TBL ( 'rights' ) ;
$qwe = << count ;
}
function has_public ()
{ foreach ( $this -> paths as $path => $Path )
{ if ( ist ( $Path -> publ ) ) { return 1 ; } }
return 0 ;
}
function has_reader ()
{ global $USER, $USER_GROUPS ;
if ( ! $this -> id ) { return 0 ; }
if ( $this -> has_public () ) { return 1 ; }
if ( ! $USER ) { return 0 ; }
foreach ( $USER_GROUPS as $name ) { $pids [ $name ] ++ ; }
foreach ( $this -> rrids as $rrid => $rr )
{ if ( $pids [ $rr -> pid ]
and ( $rr -> mod == 'ro' or $rr -> mod == 'rw' )
)
{ return 1 ; }
}
return 0 ;
}
function has_manager ()
{ global $USER ;
if ( ! $this -> id ) { return 0 ; }
if ( ! $USER ) { return 0 ; }
if ( is_adm () ) { return 1 ; }
foreach ( $this -> rrids as $rrid => $rr )
{ if ( $rr -> pid == $USER and ist ( $rr -> man ) )
{ return 1 ; }
}
return 0 ;
}
function assert_has_manager ()
{ global $USER ;
$name = $this -> repo_name () ;
if ( $this -> has_manager () )
{ return 1 ; }
else
{ html_exit ( "you are not a manager for repo $name ($USER)" ) ; }
}
function reget ()
{ $side = $this -> side ;
$ownr = $this -> ownr ;
$name = $this -> name ;
if ( $this -> id )
{ $res = get_repo ( $this -> id ) ; }
else
{ html_exit ( "can't reget repo : no id ($side,$ownr,$name)" ) ; }
return $res ;
}
function get_rights ()
{ if ( ! $this -> id )
{ $res = array () ; }
else
{ $res = get_repo_rights_by_rid ( $this -> id ) ; }
$this -> rights = $res ;
return $res ;
}
function add_owner_rights ()
{ $path_id = $this -> paths [ EPS() ] -> id ;
if ( ! $path_id )
{ html_exit ( "no path EPS for repo {$this->id}" ) ; }
$rr = new Repo_rights ;
$rr -> pid = $this -> ownr ;
$rr -> rid = $path_id ;
$rr -> mod = 'rw' ;
$rr -> wat = 't' ;
$rr -> man = 't' ;
return $rr -> insert () ;
}
function upd_url ( $txt )
{ return URL ( "index.php?ACT=UPD_REPO_FORM&RID={$this->id}", $txt ) ; }
function shw_url ( $txt = 'info' )
{ return URL ( "index.php?RID={$this->id}", $txt) ; }
function url () { return $this -> shw_url ( $this -> tag () ) ; }
function del_url ()
{ $txt = SPN ( 'delete this repository', 'red' ) ;
return URL ( "index.php?ACT=DEL&RID={$this->id}", $txt) ;
}
function svn_url ()
{ $home = REPO_HOME_URL () ;
$hrf = $home . 'repos/' . $this -> repo_name () ;
return URL ( $hrf, $hrf ) ;
}
function viewvc_url ()
{ $home = REPO_HOME_URL () ;
$hrf = $home . 'viewvc/' . $this -> repo_name () ;
return URL ( $hrf, $hrf ) ;
}
function svn_dump_url ()
{ $home = REPO_HOME_URL () ;
$hrf = $home . 'svndump/' . $this -> repo_name () ;
return URL ( $hrf, $hrf ) ;
}
function sum ()
{ return substr ( md5 ( Conf ( 'www_secret' ) . $this -> id ), 0, 8 ) ; }
function plot_url ( $atr )
{ $sum = $this -> sum () ;
$hrf = "plot.php?RID={$this->id}&SUM=$sum" ;
$atr = "BORDER=0 $atr" ;
$img = IMGA ( "$hrf&STY=SML", 'stats' , $atr ) ;
return URL ( $hrf, $img ) ;
}
function repo_name ( $styled = 0 )
{ $side = $this -> side ;
$ownr = $this -> ownr ;
$name = $this -> name ;
if ( ! $tree = try_tree_path ( $side ) )
{ html_exit ( "repo_name : no path for repo ($side,$ownr,$name)" ) ; }
$fmt = $tree -> pref ;
$fmt .= ( $fmt ? '.' : '' ) . '%n' ;
$res = preg_replace
( array ( '/%p/', '/%o/', '/%n/' )
, array ( $side , $ownr , $name )
, $fmt
) ;
if ( $styled )
{ $row = explode ( '.', $res ) ;
$cnt = count ( $row ) ;
if ( $cnt == 1 )
{ $row [ 0 ] = SPN ( $row [ 0 ], 'red' ) ; }
elseif ( $cnt > 1 )
{ $row [ 1 ] = SPN ( $row [ 1 ], 'red' ) ; } ;
$res = implode ( TT ( '.' ), $row ) ;
}
return $res ;
}
function name ()
{ return $this -> name ; }
function _a_dir ( $a )
{ return sprintf ( "%s/%s", $a, $this -> repo_name () ) ; }
function repo_dir () { return $this -> _a_dir ( REPO_DIR () ) ; }
function arch_dir () { return $this -> _a_dir ( ARCH_DIR () ) ; }
function revision ( $add_date = 0 )
{ $path = $this -> repo_dir () ;
if ( ! is_dir ( $path ) )
{ return -1 ; }
$cmd = SVN_LOOK_CMD () . ' youngest' ;
$pip = popen ( "$cmd '$path'", "r" ) ;
if ( $pip === FALSE ) { return -1 ; }
$res = trim ( fgets ( $pip, 1024 ) ) ;
pclose ( $pip ) ;
if ( $add_date )
{ $cmd = SVN_LOOK_CMD () . ' date' ;
$pip = popen ( "$cmd '$path'", "r" ) ;
if ( $pip !== FALSE )
{ $line = trim ( fgets ( $pip, 1024 ) ) ;
pclose ( $pip ) ;
$tmp = explode ( ' ', $line ) ;
$res .= sprintf ( ' → %s', $tmp[0] ) ;
}
}
return $res ;
}
function size ()
{ $stat = get_repo_stats_by_rid_last ( $this -> id ) ;
return $stat ? $stat [ 0 ] : 0 ;
}
function cnt_stats ()
{ return
( $this -> id
? cnt_repo_stats_by_rid ( $this -> id )
: 0
) ;
}
function repr ( $fld )
{ $res = $this -> $fld ;
if ( $fld == 'publ' or $fld == 'lock' )
{ $res = repr_bool ( $res ) ; }
elseif ( $fld == 'dscr' )
{ if ( $res )
{ $res = htmlspecialchars ( $res, ENT_QUOTES, 'UTF-8' ) ; }
else
{ $res = 'none' ; }
}
return $res ;
}
function txt4rights ()
{ $tab = array () ;
if ( ist ( $this -> publ ) )
{ $tab [ 'ro' ] [] = SPN ( 'anonymous', 'red' ) ; }
foreach ( $this -> rights as $pid => $rr )
{ $tags = array () ;
if ( ist ( $rr -> man ) ) { $tags [] = 'm' ; }
if ( ist ( $rr -> wat ) ) { $tags [] = 'w' ; }
$tag = ( $tags ? implode ( '.', $tags ) : '' ) ;
$tab [ $rr -> mod ] [] =
$pid . ( $tag ? " ($tag)" : '' ) ;
}
$keys = array_keys ( $tab ) ;
sort ( $keys ) ;
$txts = array () ;
$rr = new Repo_rights ;
foreach ( $keys as $key )
{ $txts [] = sprintf
( NAV ( "%s" ) . " : %s"
, $rr -> mod_tab ( $key )
, implode ( ", ", $tab [ $key ] )
) ;
}
return implode ( " ;\n", $txts ) ;
}
function info_row ()
{ global $USER ;
$atr = 'CLASS=main' ;
return TR
( ( $USER
? TDc
( ( $this -> has_manager () )
? $this -> upd_url ( 'update' )
: ( $this -> has_reader ()
? $this -> shw_url ()
: ' '
)
)
: TDc ( $this -> shw_url () )
)
. TDA ( $atr, $this -> repo_name ( 'red' ) )
. TD ( $this -> repr ( 'dscr' ) )
. ( $USER
? TDc ( $this -> has_public () ? 'yes' : 'no' )
. TD ( $this -> creat )
. TD ( $this -> ownr )
: ''
)
) ;
}
function set_defaults ()
{ global $USER, $CLAS ;
$p4c = paths4clas ( $CLAS ) ;
if ( ! count ( $p4c ) )
{ html_exit ( "set_defaults : no paths for ($USER, $CLAS)" ) ; }
$arrk = array_keys ( $p4c ) ;
$path = new Repo_paths ;
$this -> side = $arrk [ 0 ] ;
$this -> name = '' ;
$this -> ownr = $USER ;
$this -> publ = 'f' ;
$this -> lock = 'f' ;
$this -> dscr = '' ;
$this -> paths = array ( '' => $path -> set_defaults ( $this -> id ) ) ;
}
function clear_errs () { $this -> errs = array () ; }
function err ( $err ) { $this -> errs [] = $err ; }
function gen_form_new ( $act )
{ global $CLAS ;
$side = $this -> side ;
$ownr = $this -> ownr ;
$name = $this -> name ;
$dscr = $this -> dscr ;
$fields = $this -> fields ( 'new' ) ;
$new_paths = new T_tree ( paths4clas ( $CLAS, 'paths' ) ) ;
$bools = array ( 'f' => 'no', 't' => 'yes' ) ;
$txt_path = $side ;
$txt_ownr = name_login ( $ownr ) ;
$txt_name = $this -> repo_name ( 'red' ) ;
$txt_dscr = "" ;
$txt_creat = alpha_date ( $this -> creat ) ;
$txt_path = $new_paths -> input ( 'SIDx', $CLAS ) ;
$txt_name = "" ;
if ( is_adm () )
{ $txt_path = $new_paths -> input ( 'SIDx', $side ) ;
$txt_ownr = "" ;
$txt_name = "" ;
}
$top_trs = ''
. TR ( THA ( 'COLSPAN=2 CLASS=form', 'properties' ) )
. form_row ( 'opt', $fields [ 'side' ], $txt_path )
. form_row ( 'opt', $fields [ 'ownr' ], $txt_ownr )
. form_row ( 'req', $fields [ 'name' ], $txt_name )
. form_row ( 'opt', $fields [ 'dscr' ], $txt_dscr )
;
$res = <<
FORM;
return $res ;
}
function gen_form_top ( $act )
{ global $CLAS ;
if ( ! $this -> id ) { return $this -> gen_form_new ( $act ) ; }
$side = $this -> side ;
$ownr = $this -> ownr ;
$name = $this -> name ;
$lock = $this -> lock ;
$dscr = $this -> dscr ;
$fields = $this -> fields ( 'new' ) ;
$new_paths = new T_tree ( paths4clas ( $CLAS, 'paths' ) ) ;
$bools = array ( 'f' => 'no', 't' => 'yes' ) ;
$txt_path = $side ;
$txt_ownr = name_login ( $ownr ) ;
$txt_name = $this -> repo_name ( 'red' ) ;
$txt_dscr = "" ;
$txt_creat = alpha_date ( $this -> creat ) ;
$txt_apth = "" ;
$txt_upbl = form_radio_tab ( 'UPBL', $bools, 'f', '' ) ;
$txt_lock =
( is_adm ()
? form_radio_tab ( 'LOCK', $bools, $lock, '' )
: "\n"
. $this -> repr ( 'lock' )
) ;
if ( is_adm () )
{ $txt_path = $new_paths -> input ( 'SIDx', $side ) ;
$txt_ownr = "" ;
$txt_name = "" ;
}
$top_trs = ''
. TR ( THA ( 'COLSPAN=2 CLASS=form', 'properties' ) )
. ( is_adm ()
? form_row ( 'opt', $fields [ 'side' ], $txt_path )
: ''
)
. form_row ( 'opt', $fields [ 'ownr' ], $txt_ownr )
. form_row ( 'opt', $fields [ 'name' ], $txt_name )
. form_row ( 'opt', $fields [ 'dscr' ], $txt_dscr )
. form_row ( 'opt', 'revision', $this -> revision ( 1 ) )
. form_row ( 'opt', 'size', KMGB ( $this -> size () ), 3 )
. form_row ( 'opt', 'created', $txt_creat )
. form_row ( 'opt', 'locked', $txt_lock )
. ( $this -> id
? TR ( THA ( 'COLSPAN=2 CLASS=form', 'add permission path' ) )
. form_row ( 'opt', 'path', $txt_apth )
. form_row ( 'opt', 'public', $txt_upbl )
: ''
) ;
$res = <<
FORM;
return $res ;
}
# xxx
function get_form_repo ()
{ global $USER, $CLAS ;
if ( $this -> is_sub () )
{ html_exit ( "this is a sub ({$this->id})" ) ; }
$this -> clear_errs () ;
$side = trim ( $_POST [ 'SIDx' ] ) ;
$ownr = trim ( $_POST [ 'OWNR' ] ) ;
$name = trim ( $_POST [ 'NAME' ] ) ;
$lock = trim ( $_POST [ 'LOCK' ] ) ;
$dscr = trim ( $_POST [ 'DSCR' ] ) ;
$ownr = strtolower ( $ownr ) ;
if ( is_adm () )
{ $prev_name = $this -> repo_name () ;
$prev_dir = $this -> repo_dir () ;
$this -> side = $side ;
$this -> ownr = $ownr ;
$this -> name = $name ;
$this -> lock = $lock ;
$this_name = $this -> repo_name () ;
if ( $this -> id and ( $this_name != $prev_name ) )
{ if ( try_repo_by_son ( $side, $ownr, $name ) )
{ $this -> err ( "already have a repo $this_name " ) ; }
else
{ $this -> prev_dir = $prev_dir ; }
}
}
elseif ( $this -> id )
{ $side = $this -> side ;
$ownr = $this -> ownr ;
$name = $this -> name ;
$lock = $this -> lock ;
}
else
{ $ownr = $USER ;
$this -> side = $side ;
$this -> name = $name ;
$this -> ownr = $ownr ;
$lock = $this -> lock ;
}
$this -> dscr = $dscr ;
if ( ! try_tree_path ( $side ) )
{ $this -> err ( "bad side ($side)" ) ; }
if ( ! preg_match ( "/^[A-Za-z][-.\w]*$/", $name ) )
{ $this -> err ( "bad repo name ($name)" ) ; }
if ( strlen ( $name ) > 32 )
{ $this -> err ( "repo name too long (max 32) ($name)" ) ; }
if ( ! preg_match ( "/^[tf]$/", $lock ) )
{ $this -> err ( "bad lock ($lock)" ) ; }
if ( strlen ( $dscr ) > 1024 )
{ $this -> err ( "description too long (max 1024) ($dscr)" ) ; }
if ( ! preg_match ( "/^[-@.\w]+$/", $ownr ) )
{ $this -> err ( "bad string owner ($ownr)" ) ; }
elseif ( ! is_login_name ( $ownr ) )
{ $this -> err ( "unknown owner ($ownr)" ) ; }
if ( ! $this -> id )
{ if ( file_exists ( $this -> repo_dir () ) )
{ $dir = $this -> repo_dir () ;
$this -> err ( "already exists : $dir" ) ;
}
if ( try_repo_by_son ( $side, $ownr, $name ) )
{ $this -> err ( "already have repo ($side, $ownr, $name)" ) ; }
if ( ! is_adm () and $USER != $ownr )
{ $this -> err ( "USER ($USER) != owner ($ownr)" ) ; }
if ( ! array_key_exists ( $side, paths4clas ( $CLAS ) ) )
{ $this -> err ( "tree ($side) not in your class ($CLAS)" ) ; }
}
if ( $this -> id and $apth = GETVAR ( 'APTH' ) )
{ $apth = preg_replace ( '/\/+$/', '', $apth ) ;
$upbl = GETVAR ( 'UPBL' ) ;
if ( $this -> paths [ $apth ] )
{ $this -> err ( "already have a path '$apth'" ) ; }
elseif ( ! preg_match ( "/^[tf]$/", $upbl ) )
{ $this -> err ( "bad publ ($upbl)" ) ; }
else
{ $Path = new Repo_paths ;
$Path -> path = $apth ;
$Path -> rid = $this -> id ;
$Path -> publ = $upbl ;
$this -> add_path = $Path ;
}
}
return ULmk ( $this -> errs ) ;
}
function gen_form_del_ack ( $act )
{ $tag = $this -> tag () ;
$id = ( $this -> is_sub () ? $this -> Path () -> id : $this -> id ) ;
return <<
FORM;
}
function show_top ()
{ global $USER ;
$side = $this -> side ;
$ownr = $this -> ownr ;
$name = $this -> repo_name ( 'red' ) ;
$publ = $this -> repr ( 'publ' ) ;
$lock = $this -> repr ( 'lock' ) ;
$dscr = $this -> repr ( 'dscr' ) ;
$crea = $this -> repr ( 'creat' ) ;
$fields = $this -> fields ( 'new' ) ;
$atr1 = 'ALIGN=RIGHT' ;
$atr2 = 'COLSPAN=3 CLASS=opt' ;
$atr3 = '' ;
$trs = ''
. TR ( THA ( 'COLSPAN=4 CLASS=form', 'properties' ) )
. form_row ( 'opt', $fields['name'], $name, 3 )
. form_row ( 'opt', $fields['dscr'], $dscr, 3 )
. form_row ( 'opt', 'size', KMGB ( $this -> size () ), 3 )
. form_row ( 'opt', 'created', alpha_date ( $crea ), 3 )
. form_row ( 'opt', 'revision', $this -> revision ( 1 ), 3 )
. ( $USER
? form_row ( 'opt', 'owner', name_login ( $ownr ), 3 )
: ''
)
. form_row ( 'opt', 'locked' , $lock, 3 )
;
return TAB ( $trs ) ;
}
function svn_create ()
{ $name = $this -> repo_name () ;
$ownr = $this -> ownr ;
$dest = sprintf ( "%s/%s", REPO_DIR (), $name ) ;
$perl = Conf ( 'cmd_perl' ) ;
$err = 0 ;
if ( file_exists ( $dest ) )
{ return "FAIL : something exists ($dest)" ; }
$cmd = SVN_ADMIN_CMD () ;
if ( ! file_exists ( $cmd ) )
{ return "command not found ($cmd)" ; }
$sys = "$cmd create '$dest'" ;
putlog ( "$ownr $sys" ) ;
$line = system ( $sys, $err ) ;
if ( $err ) { return "FAIL : ($sys)($err,$line)" ; }
$cmd = NEW_COMMIT_HOOK () ;
$dir = Conf ( 'dir_admin' ) ;
$rid = $this -> id ;
$sys = "cd $dir ; $perl $cmd -f $rid" ;
putlog ( "$ownr $sys" ) ;
$line = system ( $sys, $err ) ;
if ( $err ) { return "FAIL : ($sys)($err,$line)" ; }
return "created repo $name" ;
}
function insert ()
{ $this -> id = $this -> next_val () ;
$res = array ( parent::insert ( $this -> fields ( 'new' ) ) ) ;
$rr = new Repo_rights ;
$rr -> pid = $this -> ownr ;
$rr -> rid = get_path_id_rid_empty ( $this -> id ) ;
$rr -> mod = 'rw' ;
$rr -> wat = 't' ;
$rr -> man = 't' ;
$res [] = $rr -> insert () ;
$res [] = $this -> svn_create () ;
# make_svnaccess_file is now called in index.php
return ULmk ( $res ) ;
}
function update ()
{ $tag4fld = $this -> fields ( is_adm () ? 'new' : 'upd' ) ;
$res = array () ;
if ( $upd = parent::update ( $tag4fld ) )
{ $res [] = $upd ; }
if ( $res and is_adm () and $this -> prev_dir )
{ $this_dir = $this -> repo_dir () ;
$prev_dir = $this -> prev_dir ;
if ( $this_dir != $prev_dir )
{ $res [] = $this -> rename ( $prev_dir ) ; }
}
if ( $this -> add_path )
{ $res [] = $this -> add_path -> insert () ; }
if ( $res ) { make_svnaccess_file () ; }
return ULor ( $res, 'no change' ) ;
}
function delete ()
{ $res = array ( parent::delete() ) ;
$res [] = $this -> archive () ;
make_svnaccess_file () ;
return ULmk ( $res ) ;
}
function archive ()
{ $name = $this -> repo_name () ;
$from = $this -> repo_dir () ;
$arch = $this -> arch_dir () ;
if ( ! is_dir ( $from ) )
{ return "FAIL : repo not found ($from,$arch)" ; }
$idx = 1 ;
$max = 1000 ;
$dst = sprintf ( "%s.%03d", $arch, $idx ) ;
while ( $idx < $max and file_exists ( $dst ) )
{ $idx ++ ; $dst = sprintf ( "%s.%03d", $arch, $idx ) ; }
if ( $idx >= $max )
{ return "FAIL : archive count exceeded ($from,$arch)" ; }
if ( ! $res = rename ( $from, $dst ) )
{ return "FAIL : rename ( $from, $dst )" ; }
else
{ touch ( $dst ) ; return "removed repo $name ($idx)" ; }
}
function rename ( $src )
{ $dst = $this -> repo_dir () ;
if ( ! is_dir ( $src ) )
{ return "FAIL : src not found (src=$src,$dst)" ; }
if ( is_dir ( $dst ) )
{ return "FAIL : dst exists ($src,dst=$dst)" ; }
if ( ! $res = rename ( $src, $dst ) )
{ return "FAIL : rename ( $src, $dst )" ; }
else
{ return "repo moved to " . $this -> repo_name () ; }
}
function load_form ()
{ return $form = <<
FORM;
}
function is_sub () { return 0 ; }
function make_sub ( $path )
{ if ( ! $this -> paths [ $path ] ) { html_exit ( "no sub ($path)" ) ; }
$res = new Sub_repo ;
return $res -> make ( $this, $path ) ;
}
function find_sub ( $path )
{ $res = '' ;
foreach ( $this -> subs as $sub )
{ if ( $sub -> path == $path ) { $res = $sub ; break ; } }
if ( ! $res ) { html_exit ( "can't find sub ($path)" ) ; }
return $res ;
}
function make_subs ()
{ $res = array () ;
foreach ( $this -> paths as $Path )
{ $res [] = $this -> make_sub ( $Path -> path ) ; }
return $res ;
}
function root () { return $this -> find_sub ( '' ) ; }
function is_root_manager () { return $this -> root () -> is_manager () ; }
function is_writer () { return $this -> root () -> is_writer () ; }
function check_managers ()
{ $this -> clear_errs () ;
foreach ( $this -> subs as $sub )
{ if ( ! $sub -> cnt_mans () )
{ $this -> err ( 'there is no manager for ' . $sub -> tag () ) ; }
}
return ULmk ( $this -> errs ) ;
}
}
################################################################
function _starts_with ( $a, $b )
{ $a_len = strlen ( $a ) ;
$b_len = strlen ( $b ) ;
if ( $b_len == 0 ) { return 1 ; }
return ( $a_len >= $b_len and substr ( $a, 0, $b_len ) == $b ) ;
}
class Sub_repo extends Repo
{ public $path = '' ;
public $rights = array () ;
static function fields ( $act )
{ return array
( 'path' => 'path'
, 'publ' => 'public'
) ;
}
function tag ()
{ return sprintf
( '%s → %s' , parent::tag(), $this -> Path () -> tag () ) ;
}
function is_sub () { return 1 ; }
function Path () { return $this -> paths [ $this -> path ] ; }
function is_public ()
{ return ist ( $this -> Path () -> publ ) ; }
function _rights ( $del = 0 )
{ $res = array () ;
ksort ( $this -> prrs ) ;
foreach ( $this -> prrs as $path => $rrs )
{ if ( _starts_with ( $this -> path, $path ) )
{ foreach ( $rrs as $pid => $rr )
{ if ( ! ( $del and ist ( $rr -> del ) ) )
{ $res [ $pid ] = $rr ; }
}
}
}
return $res ;
}
function _set_rights ()
{ $this -> rights = $this -> _rights () ; }
function make ( $parent, $path )
{ foreach ( $parent as $key => $val )
{ $this -> $key = $val ; }
$this -> path = $path ;
$this -> _set_rights () ;
return $this ;
}
function cnt_mans ()
{ $rights = $this -> _rights ( 1 ) ;
$res = 0 ;
foreach ( $rights as $pid => $rr )
{ if ( ist ( $rr -> man ) ) { $res ++ ; } }
return $res ;
}
function is_reader ()
{ global $USER, $USER_GROUPS ;
if ( $this -> is_public () ) { return 1 ; }
if ( ! $USER ) { return 0 ; }
foreach ( $USER_GROUPS as $pid )
{ if ( $rr = $this -> rights [ $pid ]
and ( $rr -> mod == 'ro' or $rr -> mod == 'rw' )
)
{ return 1 ; }
}
return 0 ;
}
function is_writer ()
{ global $USER, $USER_GROUPS ;
if ( ! $USER ) { return 0 ; }
if ( ! $this -> id ) { return 0 ; }
foreach ( $USER_GROUPS as $pid )
{ if ( $rr = $this -> rights [ $pid ] and $rr -> mod == 'rw' )
{ return 1 ; }
}
return 0 ;
}
function is_manager ()
{ global $USER ;
if ( ! $USER ) { return 0 ; }
if ( is_adm () ) { return 1 ; }
if ( ! $this -> rights [ $USER ] ) { return 0 ; }
return ist ( $this -> rights [ $USER ] -> man ) ;
}
function assert_is_manager ()
{ global $USER ;
$name = $this -> repo_name () ;
$path = $this -> path ;
$tag = $this -> tag () ;
if ( $this -> is_manager () )
{ return 1 ; }
else
{ html_exit ( "you are not a manager for $tag ($USER)" ) ; }
}
function del_url ()
{ $txt = SPN ( "delete path {$this->tag()}", 'red' ) ;
$rid = $this -> id ;
$pid = $this -> Path () -> id ;
return URL ( "index.php?ACT=DEL_PATH&RID=$rid&PID=$pid", $txt) ;
}
function svn_url ()
{ $home = REPO_HOME_URL () ;
$path = $this -> path ;
$hrf = $home . 'repos/' . $this -> repo_name ()
. ( $path ? "/$path" : '' ) ;
return URL ( $hrf, $hrf ) ;
}
function viewvc_url ()
{ $home = REPO_HOME_URL () ;
$path = $this -> path ;
$hrf = $home . 'viewvc/' . $this -> repo_name ()
. ( $path ? "/$path" : '' ) ;
return URL ( $hrf, $hrf ) ;
}
function gen_mod_tab ( $opt )
{ $path = $this -> path ;
$res = TR
( TH ( 'user/group' )
. TH ( 'delete' )
. TH ( 'rights' )
) ;
foreach ( $this -> prrs [ $path ] as $pid => $rr )
{ $atr = "CLASS=$opt" ;
$rrid = $rr -> id ;
$res .= TR
( TDA ( $atr, name_login ( $pid, '
' ) )
. TDA
( "ALIGN=CENTER $atr"
, checkbox ( "DEL[$rrid]", 'f' )
)
. TDA
( $atr
, $rr -> gen_radio ( "MOD[$rrid]" )
. ( ( ! is_a_group_name ( $pid ) )
? ' ' . checkbox ( "MAN[$rrid]", $rr -> man ) . 'manager'
. ' ' . checkbox ( "WAT[$rrid]", $rr -> wat ) . 'watcher'
: ''
)
)
) ;
}
return ( $res ) ;
}
function gen_form_sub ( $act )
{ global $CLAS ;
$path = $this -> path ;
$Path = $this -> Path () ;
$brws = $this -> svn_url () ;
$view = $this -> viewvc_url () ;
$dump = $this -> svn_dump_url () ;
$fields = $this -> fields ( 'new' ) ;
$bools = array ( 'f' => 'no', 't' => 'yes' ) ;
$txt_publ = form_radio_tab ( 'PUBL', $bools, $Path -> publ, '' ) ;
$txt_path = "path}\">" ;
$new = new Repo_rights ;
$new -> set_defaults () ;
$mod_box = $new -> gen_radio ( 'AMOD' )
. ' ' . checkbox ( 'AMAN', $this -> man ) . 'manager'
. ' ' . checkbox ( 'AWAT', $this -> wat ) . 'watcher'
;
$txt_ausr = '' ;
$upd_trs = ''
. TR ( THA ( 'COLSPAN=4 CLASS=form', $this -> tag () ) )
. ( $this -> is_reader ()
? form_row ( 'opt', 'browse, checkout', $brws, 4 )
. ( Conf ( 'viewvc' )
? form_row ( 'opt', 'view history', $view, 4 )
: ''
)
. ( $path == ''
? form_row ( 'opt', 'svn dump', $dump, 4 )
: ''
)
: ''
)
. ( $path
? form_row ( 'opt', 'path', $txt_path, 2 )
: ""
)
. form_row ( 'opt', 'public', $txt_publ, 2 )
. TR ( THA ( 'COLSPAN=3 CLASS=form', 'add user/group rights' ) )
. form_row ( 'opt', 'user/group', $txt_ausr, 2 )
. form_row ( 'opt', 'rights', $mod_box, 2 )
;
if ( count ( $this -> prrs [ $path ] ) )
{ $th = THA ( 'COLSPAN=3 CLASS=form', 'set user/group rights' ) ;
$upd_trs .= TR ( $th ) . $this -> gen_mod_tab ( 'opt' ) ;
}
$res = <<
FORM;
return $res ;
}
# has ID ; $USER is manager ; $this -> paths [ $this -> path ] exists
function get_form_sub ()
{ global $USER, $CLAS ;
$path = $this -> path ;
$Path = $this -> Path () ;
$PUBL = GETVAR ( 'PUBL' ) ;
$APTH = GETVAR ( 'APTH' ) ;
$APTH = preg_replace ( '/\/+$/', '', $APTH ) ;
if ( ! $this -> is_sub () )
{ html_exit ( "this is not a sub ({$this->id})" ) ; }
if ( ! $this -> id )
{ html_exit ( "no id for sub" ) ; }
if ( ! $Path )
{ html_exit ( "no path ($path) for sub" ) ; }
$this -> clear_errs () ;
if ( $APTH != $path and $this -> paths [ $APTH ] )
{ $this -> err ( "already have a path '$APTH'" ) ; }
else
{ $Path -> path = $APTH ; }
if ( ! preg_match ( "/^[tf]$/", $PUBL ) )
{ $this -> err ( "bad publ ($publ)" ) ; }
else
{ $Path -> publ = $PUBL ; }
if ( $ausr = strtolower ( GETVAR ( 'AUSR' ) ) )
{ if ( ! ( is_login_name ( $ausr ) or is_group_name ( $ausr ) ) )
{ $this -> err
( sprintf
( "unknown %s ($ausr)"
, ( is_a_group_name ( $ausr )
? 'group'
: 'user'
)
)
) ;
}
elseif ( cnt_repo_rights_by_pid_rid ( $ausr, $Path -> id ) )
{ $this -> err ( "already have rights for '$ausr'" ) ; }
else
{ $amod = GETVAR ( 'AMOD' ) ;
$awat = CHKVAR ( 'AWAT' ) ;
$aman = CHKVAR ( 'AMAN' ) ;
if ( ! try_repo_mod ( $amod ) )
{ $this -> err ( "unknown mod ($amod)" ) ; }
elseif ( is_a_group_name ( $ausr ) and ist ( $aman ) )
{ $this -> err ( "a group can't be a manager ; sorry" ) ; }
elseif ( is_a_group_name ( $ausr ) and ist ( $awat ) )
{ $this -> err ( "a group can't be a watcher ; sorry" ) ; }
else
{ $rr = new Repo_rights ;
$rr -> pid = $ausr ;
$rr -> rid = $this -> Path () -> id ;
$rr -> mod = $amod ;
$rr -> wat = $awat ;
$rr -> man = $aman ;
$this -> rights [ $ausr ] = $rr ;
$this -> prrs [ $path ] [ $ausr ] = $rr ;
$Path -> rrs [ $ausr ] = $rr ;
}
}
}
$repo_mods = get_repo_mods ( '', '', '' ) ;
$mods = $_POST [ 'MOD' ] ;
if ( count ( $mods ) )
{ foreach ( $mods as $rrid => $mod )
{ if ( ! $this -> rrids [ $rrid ] )
{ $this -> err ( "no existing right $rrid" ) ;
var_dump ( $this ) ;
}
elseif ( ! $repo_mods [ $mod ] )
{ $this -> err ( "bad mode ($mod)" ) ; }
else
{ $man = CHKVAR ( 'MAN', $rrid ) ;
$wat = CHKVAR ( 'WAT', $rrid ) ;
$del = CHKVAR ( 'DEL', $rrid ) ;
$pid = $this -> rrids [ $rrid ] -> pid ;
$this -> rrids [ $rrid ] -> mod = $mod ;
$this -> rrids [ $rrid ] -> man = $man ;
$this -> rrids [ $rrid ] -> wat = $wat ;
$this -> rrids [ $rrid ] -> del = $del ;
}
}
}
return ULmk ( $this -> errs ) ;
}
function show_sub ()
{ global $USER ;
$path = $this -> path ;
$Path = $this -> Path () ;
$txt_path = $Path -> tag () ;
$txt_publ = $Path -> repr_bool ( 'publ' ) ;
$fields = $this -> fields ( 'new' ) ;
$brws = $this -> svn_url () ;
$view = $this -> viewvc_url () ;
$dump = $this -> svn_dump_url () ;
$atr1 = 'ALIGN=RIGHT' ;
$atr2 = 'COLSPAN=3 CLASS=opt' ;
$atr3 = '' ;
$trs = ''
. TR ( THA ( 'COLSPAN=4 CLASS=form', 'properties' ) )
. ( $this -> is_reader ()
? form_row ( 'opt', 'browse, checkout', $brws, 3 )
. ( Conf ( 'viewvc' )
? form_row ( 'opt', 'view history', $view, 3 )
: ''
)
. ( $path == ''
? form_row ( 'opt', 'svn dump', $dump, 3 )
: ''
)
: ''
)
. form_row ( 'opt', $fields['path'], $txt_path, 3 )
. form_row ( 'opt', $fields['publ'], $txt_publ, 3 )
;
if ( $USER and count ( $this -> rights ) )
{ $trs .= TR ( THA ( 'COLSPAN=4 CLASS=form', 'user/group rights' ) ) ;
$trs .= TR
( TH ( 'user/group' )
. TH ( 'access' )
. TH ( 'manager' )
. TH ( 'watcher' )
) ;
foreach ( $this -> rights as $pid => $rr )
{ $trs .= TR
( TD ( name_login ( $pid, '
' ) )
. TDc ( $rr -> repr ( 'mod' ) )
. TDc ( $rr -> repr ( 'man' ) )
. TDc ( $rr -> repr ( 'wat' ) )
) ;
}
}
return TAB ( $trs ) ;
}
function update ()
{ $Path = $this -> Path () ;
$res = array () ;
$res [] = $Path -> update () ;
$tag = $this -> tag () ;
if ( count ( $Path -> rrs ) )
{ $res_rrs = array () ;
foreach ( $Path -> rrs as $pid => $rr )
{ if ( ! $rr -> id )
{ $res_rrs [] = $rr -> insert () ; }
elseif ( ist ( $rr -> del ) )
{ $res_rrs [] = $rr -> delete () ; }
else
{ $res_rrs [] = $rr -> update () ; }
}
$msg = ULmk ( $res_rrs ) ;
}
if ( $msg )
{ $res [] = "rights $tag :" . $msg ; }
return ULor ( $res, "$tag : no change" ) ;
}
}
################################################################
function get_repo_mods ( $from, $sql, $order )
{ $res = new Repo_mods ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
function try_repo_mod ( $s )
{ $res = get_repo_mods ( '', "mod = '$s'", 'mod' ) ;
if ( count ( $res ) == 1 )
{ return $res [ 0 ] ; }
else
{ return '' ; }
}
class Repo_mods extends Repo_table
{ function inst () { return new Repo_mods ; }
function Repo_mods () { $this -> table_init ( 'repo_mods', 'mod' ) ; }
function tag () { return $this -> mod ; }
}
################################################################
# repo paths
################################################################
function get_path ( $id )
{ $qwe = "id = '$id'" ;
$res = new Repo_paths ;
return $res -> db_get1 ( '', $qwe, 'id' ) ;
}
function get_path_id_rid_empty ( $rid )
{ $qwe = "rid = '$rid' and path = ''" ;
$res = new Repo_paths ;
return $res -> db_get1 ( '', $qwe, 'id' ) -> id ;
}
class Repo_paths extends Repo_table
{ public $id ;
public $rid ;
public $path ;
public $publ ;
static $fields_bool = array ( 'publ' => 1 ) ;
function inst () { return new Repo_paths ; }
function Repo_paths () { $this -> table_init ( 'repo_paths', 'id' ) ; }
function tag ()
{ return ( $this -> path ? $this -> path : 'repository root' ) ; }
function class_name () { return 'path' ; }
function fields ()
{ return array
( rid => 'repo id'
, path => 'repo path'
, publ => 'public'
) ;
}
function all_fields ()
{ return array
( id => 'id'
, rid => 'repo id'
, path => 'repo path'
, publ => 'public'
) ;
}
function set_defaults ( $rid )
{ $this -> rid = $rid ;
$this -> path = '' ;
$this -> publ = 'f' ;
return $this ;
}
function repr ( $fld )
{ $res = $this -> $fld ;
if ( $fld == 'publ' )
{ $res = repr_bool ( $res ) ; }
return $res ;
}
function insert ()
{ return parent::insert () ; }
function update ()
{ return parent::update () ; }
function delete ()
{ return ULmk ( parent::delete () ) ; }
}
################################################################
# repo rights
################################################################
function get_repo_right ( $id )
{ $res = new Repo_rights ;
$res -> db_get_id ( $id ) || html_exit ( "can't get repo_right ($id)" ) ;
return $res ;
}
function get_repo_rights ( $from, $sql, $order )
{ $res = new Repo_rights ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
function cnt_repo_rights ( $from, $sql )
{ $res = new Repo_rights ;
return $res -> db_get_cnt ( $from, $sql ) ;
}
function get_repo_rights_by_pid ( $pid )
{ return get_repo_rights ( '', "pid = '$pid'", 'pid' ) ; }
function get_repo_rights_by_rid ( $rid )
{ $rrs = get_repo_rights ( '', "rid = '$rid'", 'pid' ) ;
$res = array () ;
foreach ( $rrs as $rr ) { $res [ $rr -> pid ] = $rr ; }
return $res ;
}
function cnt_repo_rights_by_pid_rid ( $pid, $rid )
{ return cnt_repo_rights ( '', "pid = '$pid' and rid = '$rid'" ) ; }
class Repo_rights extends Repo_table
{ static $mod_tab = array
( 'ro' => 'read'
, 'rw' => 'commit'
, 'nx' => 'none'
) ;
static $fields_bool = array
( 'man' => 1
, 'wat' => 1
) ;
function inst () { return new Repo_rights ; }
function Repo_rights () { $this -> table_init ( 'repo_rights', 'id' ) ; }
function class_name () { return "rights" ; }
function tag ()
{ $atr = array ( $this -> mod ) ;
if ( ist ( $this -> man ) ) { $atr [] = 'manager' ; }
if ( ist ( $this -> wat ) ) { $atr [] = 'watcher' ; }
return sprintf ( "%s (%s)", $this -> pid, implode ( ',', $atr ) ) ;
}
static function fields ()
{ return array
( 'pid' => 'login'
, 'rid' => 'repository'
, 'mod' => 'mode'
, 'man' => 'manager'
, 'wat' => 'watcher'
) ;
}
static function all_fields ()
{ $res = self::fields () ;
$res [ 'id' ] = 'id' ;
return $res ;
}
function set_defaults ()
{ $this -> pid = '' ;
$this -> rid = '' ;
$this -> mod = 'ro' ;
$this -> man = 'f' ;
$this -> wat = 'f' ;
}
function mod_tab ( $mod )
{ return self::$mod_tab [ $mod ] ; }
function repr ( $fld )
{ $res = $this -> $fld ;
if ( $fld == 'mod' )
{ $res = $this -> mod_tab ( $this -> mod ) ; }
elseif ( self::$fields_bool [ $fld ] )
{ $res = repr_bool ( $res ) ; }
return $res ;
}
function gen_radio ( $name )
{ $pid = $this -> pid ;
$rid = $this -> rid ;
$mod = $this -> mod ;
$rr_tab = array
( 'ro' => NAV ( 'read' )
, 'rw' => NAV ( 'commit' )
, 'nx' => NAV ( 'none' )
) ;
return form_radio_tab ( $name, $rr_tab, $mod, '' ) ;
}
function get_form ( $repo )
{ $mods = $_POST [ 'MOD' ] ;
foreach ( $mods as $idx => $mod )
{ if ( ! $rights [ $idx ] )
{ $res .= LI ( "no existing right $idx" ) ; }
else
{ $repo -> rights [ $idx ] -> mod = $mod ; }
}
return ( $res ? LI ( UL ( $res ) ) : '' ) ;
}
function insert ()
{ $res = parent::insert () ;
$tag = $this -> tag () ;
return $res ? "rights $tag : " . ULmk ( $res ) : '' ;
}
function update ()
{ return parent::update () ;
$res = parent::update () ;
$tag = $this -> tag () ;
return $res ? "rights $tag : " . ULmk ( $res ) : '' ;
}
function delete ()
{ $res = parent::delete () ;
$tag = $this -> tag () ;
return $res ? "rights $tag : " . ULmk ( $res ) : '' ;
}
}
################################################################
# repo groups
################################################################
function try_repo_group ( $gid )
{ $res = new Repo_group ;
if ( ! $res -> db_get_id ( $gid ) )
{ $res = '' ; }
else
{ $res -> get_group_mems () ; }
return $res ;
}
function get_repo_group ( $gid )
{ if ( ! ( $res = try_repo_group ( $gid ) ) )
{ html_exit ( "can't get repo_group ($gid)" ) ; }
return $res ;
}
function try_repo_group_by_name ( $name )
{ $qwe = "name = '$name' " ;
$res = get_repo_groups ( '', $qwe, 'gid' ) ;
if ( count ( $res ) == 1 )
{ return $res [ 0 ] ; }
else
{ return '' ; }
}
function get_repo_groups ( $from, $sql, $order )
{ $sel = new Repo_group ;
$res = $sel -> db_get_all ( $from, $sql, $order ) ;
if ( count ( $res ) )
{ $idx = array () ;
foreach ( $res as $repo_group )
{ $repo_group -> mems = array () ;
$idx [ $repo_group -> gid ] = $repo_group ;
}
$gids = array_keys ( $idx ) ;
$qwe = sprintf ( 'gid in ( %s )' , implode ( ',', $gids ) ) ;
$mems = get_group_mems ( '', $qwe, 'pid' ) ;
foreach ( $mems as $mem )
{ $idx [ $mem -> gid ] -> mems [ $mem -> pid ] = $mem ; }
}
return $res ;
}
function user_group_ids ()
{ global $USER ;
$res = array () ;
if ( $USER )
{ $res [] = $USER ;
$Groups = TBL ( 'groups' ) ;
$Mems = TBL ( 'gr_mem' ) ;
$qwe = << name ; }
}
return $res ;
}
function get_groups_xusers ( $order, $pid = '' )
{ $tab_groups = TBL ( 'repo_groups' ) ;
$tab_mems = TBL ( 'repo_group_members' ) ;
$tab_xusers = TBL ( 'repo_xusers' ) ;
$sub =
( $pid
? "$tab_mems.pid = '$pid' "
: "$tab_mems.pid in ( SELECT pid from $tab_xusers ) "
) ;
$qwe = "$tab_groups.gid = $tab_mems.gid and $sub" ;
return get_repo_groups ( $tab_mems , $qwe , $order ) ;
}
function repo_groups_url ()
{ return URL ( "groups.php", 'groups' ) ; }
function new_repo_group_url ()
{ return URL ( "groups.php?ACT=NEW_GRP_FORM", 'create a new group' ) ; }
function repo_groups_info_head ()
{ global $USER ;
return TR
( ( $USER ? TH ( 'info' ) : '' )
. TH ( 'group' )
. TH ( 'owner' )
. TH ( 'created' )
. TH ( 'members' )
) ;
}
function may_create_repo_group ()
{ global $CLAS ; return $CLAS != 'guests' ; }
class Repo_group extends Repo_table
{ var $mems = array () ;
function inst () { return new Repo_group ; }
function Repo_group () { $this -> table_init ( 'repo_groups', 'gid' ) ; }
function next_val () { return parent::_next_val () ; }
function tag () { return $this -> name ; }
function class_name () { return "group" ; }
function fields ( $act )
{ $res = array () ;
if ( $act == 'new' )
{ $res [ 'name' ] = 'name' ;
$res [ 'ownr' ] = 'owner' ;
}
elseif ( $act == 'upd' )
{ if ( is_adm () )
{ $res [ 'ownr' ] = 'owner' ; }
}
else
{ html_exit ( "bad act for fields ($act)" ) ; }
return $res ;
}
function is_manager ()
{ global $USER ;
$res = 0 ;
if ( is_adm ()
or ( $this -> mems [ $USER ]
and ist ( $this -> mems [ $USER ] -> man )
)
)
{ $res = 1 ; }
return $res ;
}
function assert_is_manager ()
{ global $USER ;
if ( $this -> is_manager () )
{ return 1 ; }
else
{ $name = $this -> name ;
html_exit ( "you ($USER) are not a manager for this group ($name)" ) ;
}
}
function reget ()
{ $name = $this -> name ;
if ( $this -> gid )
{ $res = get_repo_group ( $this -> gid ) ; }
else
{ html_exit ( "can't reget repo_group ($name) : no gid" ) ; }
return $res ;
}
function is_used ()
{ return cnt_repo_rights ( '', "pid = '{$this->name}'" ) ; }
function get_group_mems ()
{ if ( ! $this -> gid )
{ $res = array () ; }
else
{ foreach ( get_group_mems ( '', "gid = {$this->gid}", 'pid' ) as $mem )
{ $this -> mems [ $mem -> pid ] = $mem ; }
}
}
function url ( $txt )
{ return URL ( "groups.php?ACT=SHW_GRP&GID={$this->gid}", $txt ) ; }
function upd_url ( $txt )
{ return URL ( "groups.php?ACT=UPD_GRP_FORM&GID={$this->gid}", $txt ) ; }
function del_url ()
{ $txt = SPN ( 'delete this group', 'red' ) ;
return URL ( "groups.php?ACT=DEL&GID={$this->gid}", $txt ) ;
}
function repr ( $fld )
{ $res = $this -> $fld ;
if ( $fld == 'mem' or $fld == 'man' )
{ $res = repr_bool ( $res ) ; }
return $res ;
}
function txt4mems ()
{ $tab = array () ;
$mems = $this -> mems ;
if ( ! count ( $mems ) ) { return ITA ( 'none' ) ; }
foreach ( $mems as $idx => $mem )
{ if ( ist ( $mem -> mem ) )
{ $tab [ 'member' ] [ $mem -> pid ] ++ ; }
if ( ist ( $mem -> man ) )
{ $tab [ 'manager' ] [ $mem -> pid ] ++ ; }
}
$keys = array_keys ( $tab ) ;
sort ( $keys ) ;
$txts = array () ;
foreach ( $keys as $key )
{ $pids = array_keys ( $tab [ $key ] ) ;
sort ( $pids ) ;
$txts [] = sprintf
( NAV ( "%s" ) . " : %s"
, $key
, implode ( ", ", $pids )
) ;
}
return implode ( " ;\n", $txts ) ;
}
function info_row ()
{ global $USER ;
$atr = 'CLASS=main' ;
return TR
( ( $USER
? TD
( ( $this -> is_manager () )
? $this -> upd_url ( 'update' )
: ' '
)
: ''
)
. TDA ( $atr, $this -> name )
. TD ( $this -> ownr )
. TD ( $this -> creat )
. TD ( $this -> txt4mems () )
) ;
}
function set_defaults ()
{ global $USER ;
$this -> name = '' ;
$this -> ownr = $USER ;
}
function gen_mem_tab ()
{ $res = TR
( TH ( 'user' )
. TH ( 'member' )
. TH ( 'manager' )
) ;
foreach ( $this -> mems as $pid => $mem )
{ $res .= $mem -> input ( "MEM[$pid]" ) ; }
return $res ;
}
function gen_form ( $act )
{ $name = $this -> name ;
$name = ( $name ? $name : '@' ) ;
$ownr = $this -> ownr ;
$fields = $this -> fields ( 'new' ) ;
$creat = $this -> creat ;
if ( is_adm () )
{ $txt_ownr = "" ; }
else
{ $txt_ownr = name_login ( $ownr )
. "" ;
}
if ( ! $this -> gid )
{ $txt_name = "" ; }
else # update
{ $txt_name = $name . "" ;
$txt_ausr = '' ;
$mems = new Repo_mems ;
$mems -> set_defaults () ;
$txt_rr = ''
. TR ( THA ( 'COLSPAN=3 CLASS=form', 'add a member' ) )
. form_row ( 'opt', 'user', $txt_ausr, 2 )
. $mems -> input_fld ( 'AMAN', 'man' )
. ( count ( $this -> mems )
? TR ( THA ( 'COLSPAN=3 CLASS=form', 'members' ) )
. $this -> gen_mem_tab ()
: ''
)
;
}
$optn = ( $this -> gid ? 'opt' : 'req' ) ;
$txt_form = ''
. TR ( THA ( 'COLSPAN=3 CLASS=form', 'repo group properties' ) )
. form_row ( $optn, $fields [ 'name' ], $txt_name, 2 )
. form_row ( 'opt', $fields [ 'ownr' ], $txt_ownr, 2 )
. ( $this -> gid ? form_row ( 'opt', 'created', $creat, 2 ) : '' )
;
$res = <<
FORM;
return $res ;
}
function get_form ()
{ global $USER ;
$res = '' ;
$name = trim ( $_POST [ 'NAME' ] ) ;
$ownr = trim ( $_POST [ 'OWNR' ] ) ;
$BOOL = new T_bool ;
if ( is_adm () )
{ $this -> name = $name ;
$this -> ownr = $ownr ;
}
elseif ( ! $this -> gid )
{ $this -> name = $name ;
$ownr = $this -> ownr ;
}
else
{ $name = $this -> name ;
$ownr = $this -> ownr ;
}
if ( ! preg_match ( "/^@/", $name ) )
{ $res .= LI ( "group name must start with '@' ($name)" ) ; }
if ( ! preg_match ( "/^@[a-z][a-z0-9_]*$/", $name ) )
{ $res .= LI ( "group name must be lowercase alpha-numeric ($name)" ) ; }
if ( strlen ( $name ) > 32 )
{ $res .= LI ( "group name too long (max 32) ($name)" ) ; }
if ( ! $this -> gid and try_repo_group_by_name ( $name ) )
{ $res .= LI ( "group ($name) already exists" ) ; }
if ( $ownr == '' )
{ $res .= LI ( "should not be empty : owner" ) ; }
if ( is_adm () and ! is_login_name ( $ownr ) )
{ $res .= LI ( "unknown user ($ownr)" ) ; }
if ( $this -> gid and $ausr = strtolower ( GETVAR ( 'AUSR' ) ) )
{ $mem = new Repo_mems ;
$mem -> from_form ( 'AUSR', 'AMEM', 'AMAN' ) ;
if ( ! is_login_name ( $ausr ) )
{ $res .= LI ( "unknown user ($ausr)" ) ; }
elseif ( get_group_mems_by_pid_gid ( $ausr, $this -> gid ) )
{ $res .= LI ( "already have member '$ausr'" ) ; }
else
{ $amem = GETVAR ( 'AMEM' ) ;
$aman = GETVAR ( 'AMAN' ) ;
if ( ! $BOOL -> vals [ $amem ] )
{ $res .= LI ( "bad member val $amem" ) ; }
elseif ( ! $BOOL -> vals [ $aman ] )
{ $res .= LI ( "bad manager val $aman" ) ; }
else
{ $mem = new Repo_mems ;
$mem -> gid = $this -> gid ;
$mem -> pid = $ausr ;
$mem -> mem = $amem ;
$mem -> man = $aman ;
$this -> add_mem = $mem ;
}
}
}
if ( $this -> gid )
{ $mems = $_POST [ 'MEM' ] ;
$mans = $_POST [ 'MAN' ] ;
$mem_cnt = 0 ;
if ( $this -> add_mem and ist ( $this -> add_mem -> man ) )
{ $mem_cnt ++ ; }
if ( count ( $mems ) )
{ foreach ( $mems as $pid => $val )
{ $mem = $mems [ $pid ] ;
$man = $mans [ $pid ] ;
if ( ! $this -> mems [ $pid ] )
{ $res .= LI ( "no existing member $pid" ) ; }
elseif ( ! $BOOL -> vals [ $mem ] )
{ $res .= LI ( "bad member val $mem" ) ; }
elseif ( ! $BOOL -> vals [ $man ] )
{ $res .= LI ( "bad manager val $man" ) ; }
else
{ $this -> mems [ $pid ] -> mem = $mem ;
$this -> mems [ $pid ] -> man = $man ;
if ( ist ( $man ) ) { $mem_cnt ++ ; }
}
}
}
if ( $mem_cnt == 0 )
{ $res .= LI ( "you can't remove all managers" ) ; }
}
return ( $res ? UL ( $res . LI ( 'nothing is updated' ) ) : '' ) ;
}
function gen_form_del_ack ( $act )
{ $name = $this -> name ;
return <<
FORM;
}
function insert ()
{ global $USER ;
$this -> gid = $this -> next_val () ;
$fields = $this -> fields ( 'new' ) ;
$fields [ 'gid' ] = 'gid' ;
$res = array ( parent::insert ( $fields ) ) ;
$mem = new Repo_mems ;
$mem -> gid = $this -> gid ;
$mem -> pid = $USER ;
$mem -> mem = 't' ;
$mem -> man = 't' ;
$res [] = $mem -> insert () ;
make_svnaccess_file () ;
return ULmk ( $res ) ;
}
function update ()
{ $fields = $this -> fields ( is_adm () ? 'new' : 'upd' ) ;
$res = parent::update ( $fields ) ;
$mes = array () ;
if ( $this -> add_mem )
{ $mes [] = $this -> add_mem -> insert () ; }
if ( count ( $this -> mems ) )
{ foreach ( $this -> mems as $pid => $mem )
{ if ( $mem -> mem == 'f' and $mem -> man == 'f' )
{ $mes [] = $mem -> delete () ; }
else
{ $msg = $mem -> update () ;
if ( $msg ) { $mes [] = $msg ; }
}
}
}
if ( $mes ) { $res [] = 'members:' . ULmk ( $mes ) ; }
make_svnaccess_file () ;
return ULmk ( $res ? $res : 'no change' ) ;
}
function delete ()
{ $res = parent::delete () ;
make_svnaccess_file () ;
return ULmk ( $res ) ;
}
}
################################################################
function get_group_mem ( $id )
{ $res = new Repo_mems ;
$res -> db_get_id ( $id ) || html_exit ( "can't get group_mem ($id)" ) ;
return $res ;
}
function get_group_mems ( $from, $sql, $order )
{ $res = new Repo_mems ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
function get_group_mems_by_pid_gid ( $pid, $gid )
{ return get_group_mems ( '', "pid = '$pid' and gid = '$gid'", '' ) ; }
class Repo_mems extends Repo_table
{ static $fields_bool = array
( 'mem' => 1
, 'man' => 1
) ;
function inst () { return new Repo_mems ; }
function Repo_mems ()
{ $this -> table_init ( 'repo_group_members', 'id' ) ; }
function tag ()
{ $res = $this -> pid ;
if ( ist ( $this -> man ) ) { $res .= ' (manager)' ; }
return $res ;
}
function class_name () { return "group member" ; }
function fields ()
{ return array
( 'gid' => 'group id'
, 'pid' => 'login'
, 'mem' => 'member?'
, 'man' => 'manager?'
) ;
}
function set_defaults ()
{ $this -> gid = '' ;
$this -> pid = '' ;
$this -> mem = 't' ;
$this -> man = 'f' ;
}
function repr ( $fld )
{ $res = $this -> $fld ;
if ( self::$fields_bool [ $fld ] )
{ $res = repr_bool ( $res ) ; }
return $res ;
}
function input_fld ( $name, $fld )
{ global $USER ;
$fields = $this -> fields () ;
$atr = 'CLASS=opt COLSPAN=2' ;
$bool = new T_bool ;
return TR
( THr ( $fields [ $fld ] )
. TDA ( $atr, $bool -> input ( $name, $this -> $fld ) )
) ;
}
function input ()
{ global $USER ;
$fields = $this -> fields () ;
$bool = new T_bool ;
$mem = $this -> mem ;
$man = $this -> man ;
if ( $pid = $this -> pid )
{ $atr = 'CLASS=opt' ;
return TR
( TD ( name_login ( $pid ) )
. TDA ( $atr, $bool -> input ( "MEM[$pid]", $mem ) )
. TDA ( $atr, $bool -> input ( "MAN[$pid]", $man ) )
) ;
}
}
function from_form ( $nm_usr, $nm_mem, $nm_man )
{ $usr = new T_txt ;
$mem = new T_bool ;
$man = new T_bool ;
$usr -> fld_from_form ( $this, 'pid', $nm_usr ) ;
$mem -> fld_from_form ( $this, 'mem', $nm_mem ) ;
$man -> fld_from_form ( $this, 'man', $nm_man ) ;
}
}
################################################################
function get_repo_roots ( $from, $sql, $order )
{ $res = new Repo_roots ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
function try_repo_root ( $pid )
{ $res = get_repo_roots ( '', "pid = '$pid'", 'pid' ) ;
if ( count ( $res ) == 1 )
{ return $res [ 0 ] ; }
else
{ return '' ; }
}
function actv_root ( $actv )
{ global $USER, $EDS ;
if ( ! $root = try_repo_root ( $USER ) )
{ html_exit ( "you ($USER) are not an admin" ) ; }
elseif ( $root -> actv != $actv )
{ $root -> actv = $actv ;
$root -> update () ;
$EDS [ $USER ] = ist ( $actv ) ;
}
}
class Repo_roots extends Repo_table
{ function inst () { return new Repo_roots ; }
function Repo_roots () { $this -> table_init ( 'repo_roots', 'pid' ) ; }
function tag () { return $this -> pid ; }
function update ()
{ $tab = array ( $this -> actv ) ;
$qwe = sprintf
( "UPDATE %s SET actv = $1 WHERE pid = '%s' ; "
, $this -> _tab
, $this -> pid
) ;
db_query_params ( $qwe, $tab ) ;
}
}
################################################################
function paths4clas ( $clas, $paths = 0 )
{ $trees = Conf ( 'trees' ) ;
$is_adm = is_adm () ;
$res = array () ;
foreach ( $trees as $tree )
{ if ( $is_adm or $tree -> crea [ $clas ] )
{ $path = $tree -> path ;
$res [ $path ] = ( $paths ? $path : $tree ) ;
}
}
return $res ;
}
function try_tree_path ( $b )
{ $trees = Conf ( 'trees' ) ;
$res = '' ;
foreach ( $trees as $tree )
{ if ( $tree -> path == $b )
{ $res = $tree ; break ; }
}
return $res ;
}
class Repo_tree
{ # $creas is an array of class names ; checked by Config::make_trees()
function init ( $path, $pref, $creas )
{ $this -> path = $path ;
$this -> pref = $pref ;
$this -> crea = array () ;
foreach ( $creas as $idx => $clas )
{ $this -> crea [ $clas ] ++ ; }
return $this ;
}
function dmp ()
{ printf
( "tree path (%s) pref (%s) crea (%s)
\n"
, $this -> path , $this -> pref
, implode ( '|', array_keys ( $this -> crea ) )
) ;
}
}
################################################################
class Uclas
{ function init ( $x )
{ $this -> clas = $x [ 'clas' ] ;
$this -> dscr = $x [ 'dscr' ] ;
$this -> typ = $x [ 'typ' ] ;
}
function dmp ()
{ printf ( "user typ (%s) " , $this -> typ ) ; }
}
class Uclas_table extends Uclas
{ function init ( $x )
{ parent::init ( $x ) ;
$this -> tab = $x [ 'table' ] ;
$this -> login = $x [ 'login' ] ;
$this -> paswd = $x [ 'paswd' ] ;
$this -> cname = $x [ 'cname' ] ;
return $this ;
}
function dmp ()
{ parent::dmp () ;
printf
( "tab (%s) login (%s) paswd (%s)
\n"
, $this -> tab, $this -> login, $this -> paswd
) ;
}
}
################################################################
class Uclas_ldap extends Uclas
{ function init ( $x )
{ parent::init ( $x ) ;
$this -> tag = $x [ 'tag' ] ;
$this -> ldap = $x [ 'ldap' ] ;
$this -> pat = $x [ 'pat' ] ;
return $this ;
}
function dmp ()
{ parent::dmp () ;
printf
( "tag (%s) serv (%s) base (%s) pat (%s)
\n"
, $this -> tag
, $this -> ldap -> serv
, $this -> ldap -> base
, $this -> pat
) ;
}
}
################################################################
function get_repo_stats ( $from, $sql, $order )
{ $res = new Repo_stats ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
function get_repo_stats_by_rid ( $rid )
{ return get_repo_stats ( '', "rid = '$rid'", 'date' ) ; }
function cnt_repo_stats_by_rid ( $rid )
{ $sel = new Repo_stats ;
return $sel -> db_get_cnt ( '', "rid = '$rid'" ) ;
}
function get_repo_stats_by_rid_last ( $rid )
{ return get_repo_stats ( '', "rid = '$rid'", 'date desc limit 1' ) ; }
class Repo_stats extends Repo_table
{ function inst () { return new Repo_stats ; }
function Repo_stats () { $this -> table_init ( 'repo_stats', 'id' ) ; }
function tag () { return $this -> date ; }
}
################################################################
function try_repo_xuser ( $id )
{ $res = new Repo_xuser ;
if ( ! $res -> db_get_id ( $id ) ) { $res = '' ; }
return $res ;
}
function get_repo_xuser ( $id )
{ if ( ! ( $res = try_repo_xuser ( $id ) ) )
{ html_exit ( "can't get repo_xuser ($id)" ) ; }
return $res ;
}
function get_repo_xusers ( $from, $sql, $order )
{ $res = new Repo_xuser ;
return $res -> db_get_all ( $from, $sql, $order ) ;
}
class Repo_xuser extends Repo_table
{ function inst () { return new Repo_xuser ; }
function Repo_xuser () { $this -> table_init ( 'repo_xusers', 'id' ) ; }
function tag () { return $this -> pid ; }
function class_name () { return "xuser" ; }
function delete_url ()
{ $id = $this -> id ;
$pid = $this -> pid ;
$hrf = "stats.php?ACT=DEL&ID=$id" ;
return URL ( $hrf, SPN ( "delete xuser $pid", 'red' ) ) ;
}
function delete_rrs_mems_url ()
{ $id = $this -> id ;
$pid = $this -> pid ;
$hrf = "stats.php?ACT=DEL_RRS&ID=$id" ;
$txt = "delete rights/memberships xuser $pid" ;
return URL ( $hrf, SPN ( $txt, 'red' ) ) ;
}
function delete ()
{ $pid = $this -> pid ;
$res = array () ;
# $repos = get_repos ( '', "ownr = '$pid'",'side,ownr,name' ) ;
# foreach ( $repos as $idx => $repo )
# { $name = $repo -> repo_name () ;
# $res [] = $repo -> delete () ;
# }
$repos = get_repos_xusers ( 'side,ownr,name', $pid ) ;
foreach ( $repos as $idx => $repo )
{ foreach ( $repo -> subs as $sub )
{ if ( $rr = $sub -> rights [ $pid ] )
{ $res [] = $sub -> tag () . ' : ' . $rr -> delete () ; }
}
}
$groups = get_groups_xusers ( 'name', $pid ) ;
foreach ( $groups as $idx => $group )
{ if ( $mem = $group -> mems [ $pid ] )
{ $res [] = $group -> tag() . ' : ' . $mem -> delete () ; }
}
$res [] = parent::delete () ;
return ULmk ( $res ) ;
}
function delete_rrs_mems ()
{ $pid = $this -> pid ;
$res = array () ;
$repos = get_repos_xusers ( 'side,ownr,name', $pid ) ;
foreach ( $repos as $idx => $repo )
{ foreach ( $repo -> subs as $sub )
{ if ( $rr = $sub -> rights [ $pid ] )
{ $res [] = $sub -> tag () . ' : ' . $rr -> delete () ; }
else
{ var_dump ( $sub -> rights ) ;
html_exit ( "stats delete_rrs : no rr for pid $pid" ) ;
}
}
}
$groups = get_groups_xusers ( 'name', $pid ) ;
foreach ( $groups as $idx => $group )
{ if ( $mem = $group -> mems [ $pid ] )
{ $res [] = $group -> tag() . ' : ' . $mem -> delete () ; }
}
return ULmk ( $res ) ;
}
}
################################################################
class Ftyp
{ function inst () { return new Ftyp ; }
function typ_init ( $typ ) { $this -> _typ = $typ ; }
function pat () { return '' ; }
function selectbox ( $name, $pval, $vals )
{ $res = "\n" ;
return $res ;
}
function input ( $name, $pval )
{ if ( $vals = $this -> vals )
{ $cnt = count ( $vals ) ;
if ( ! $vals [ $pval ] )
{ $keys = array_keys ( $vals ) ; $pval = $keys [ 0 ] ; }
if ( $cnt == 1 )
{ $res = $pval ;
$res .= sprintf
( '' . "\n"
, $name
, $pval
) ;
}
elseif ( $cnt < 4 )
{ $res = form_radio_tab ( $name, $vals, $pval, '' ) ; }
elseif ( $cnt < 6 )
{ $res = form_radio_tab ( $name, $vals, $pval ) ; }
else
{ $res = $this -> selectbox ( $name, $pval, $vals ) ; }
}
else
{ $res = "\n" ;
}
return $res ;
}
function check ()
{ $res = 1 ;
$val = $this -> val ;
if ( $vals = $this -> vals () )
{ $res = array_key_exists ( $val, $vals ) ; }
elseif ( $pat = $this -> pat () )
{ $res = preg_match ( $pat, $val ) ; }
return $res ;
}
function repr ( $val )
{ $res = $val ;
if ( $vals = $this -> vals () )
{ if ( array_key_exists ( $val, $vals ) )
{ $res = $vals [ $val ] ; }
}
return $res ;
}
function from_form ( $name )
{ $res = '' ;
$matches = array () ;
if ( preg_match ( '/^([A-Za-z]+)\[([0-9]*)\]/', $name, $matches ) )
{ $nam = $matches [ 1 ] ;
$idx = $matches [ 2 ] ;
$val = $_POST [ $nam ] [ $idx ] ;
echo "nam $nam idx $idx val $val
\n" ;
}
else
{ $val = $_POST [ $name ] ; }
$res = htmlentities ( trim ( $val ) ) ;
$this -> val = $res ;
return $res ;
}
function fld_from_form ( $obj, $fld, $name )
{ $res = '' ;
$matches = array () ;
if ( preg_match ( '/^([A-Za-z]+)\[([0-9]*)\]/', $name, $matches ) )
{ $nam = $matches [ 1 ] ;
$idx = $matches [ 2 ] ;
if ( array_key_exists ( $nam, $_POST )
and array_key_exists ( $idx, $_POST [ $nam ] )
)
{ $val = $_POST [ $nam ] [ $idx ] ;
echo "nam $nam idx $idx val $val
\n" ;
$res = htmlentities ( $val ) ;
$obj -> $fld = $res ;
$this -> val = $res ;
}
}
elseif ( array_key_exists ( $name, $_POST ) )
{ $val = $_POST [ $name ] ;
$res = htmlentities ( trim ( $_POST [ $name ] ) ) ;
$obj -> $fld = $res ;
$this -> val = $res ;
}
return $res ;
}
}
class T_typs extends Ftyp
{ var $vals = array
( 'T_bool' => 'bool'
, 'T_int' => 'integer'
, 'T_txt' => 'text'
, 'T_alpha' => 'alpha'
) ;
function T_typs () { $this -> typ_init ( 'T_typs' ) ; }
}
class T_bool extends Ftyp
{ var $vals = array
( 't' => 'yes'
, 'f' => 'no'
) ;
function T_bool () { $this -> typ_init ( 'T_bool' ) ; }
}
class T_txt extends Ftyp
{ function T_txt () { $this -> typ_init ( 'T_txt' ) ; }
}
class T_alpha extends T_txt
{ function T_alpha () { $this -> typ_init ( 'T_alpha' ) ; }
function pat () { return '^[A-Za-z][A-Za-z0-9_]*$' ; }
}
class T_int extends Ftyp
{ function T_int () { $this -> typ_init ( 'T_int' ) ; }
function pat () { return '/^-?[0-9]+$/' ; }
}
class T_tree extends Ftyp
{ var $vals ;
function T_tree ( $vals = array () )
{ $this -> typ_init ( 'T_tree' ) ;
$this -> vals = $vals ;
}
}