source: extensions/Register_FluxBB/include/functions.inc.php @ 22039

Last change on this file since 22039 was 22039, checked in by Eric, 11 years ago
  • Bug fixed : Admins passwords synchronization between FluxBB and Piwigo when changed
  • Bug fixed : Password synchronization between FluxBB and Piwigo if a user uses Piwigo's password recovery system
  • Bug fixed : Exclude password comparison from audit
  • Todo : Recode synch, migration and audit actions for existing users before plugin activation - Have to take care on passwords !
  • Property svn:eol-style set to LF
File size: 16.6 KB
RevLine 
[17457]1<?php
2
3include_once (PHPWG_ROOT_PATH.'/include/constants.php');
4include_once (REGFLUXBB_PATH.'include/constants.php');
5
6
7function Register_FluxBB_admin_menu($menu)
8{
9  array_push($menu, array(
10    'NAME' => 'Register FluxBB',
11    'URL' => get_root_url().'admin.php?page=plugin-'.basename(REGFLUXBB_PATH)
12    )
13  );
14  return $menu;
15}
16
[22039]17/**
18 * Add new registered user in FluxBB user table
19 */
[17457]20function Register_FluxBB_Adduser($register_user)
21{
22  global $errors, $conf;
23       
24  // Exclusion of Adult_Content users
25  if ($register_user['username'] != "16" and $register_user['username'] != "18")
26  {
[22039]27    // Warning : FluxBB uses Sha1 hash instead of a salted md5 for Piwigo! // TODO: Reset password
[17457]28    FluxBB_Adduser($register_user['id'], $register_user['username'], sha1($_POST['password']), $register_user['email']);
29  }
30}
31
[22039]32/**
33 * Delete registered user in FluxBB user table
34 */
[17457]35function Register_FluxBB_Deluser($user_id)
36{
37  FluxBB_Deluser(FluxBB_Searchuser($user_id), true);
38}
39
[22039]40/**
41 * Change user's password in FluxBB user table if a new password is set in Piwigo
42 */
[17457]43function Register_FluxBB_InitPage()
44{
45  global $conf, $user;
46
[22039]47  if (isset($_POST['validate']))
[17457]48  {
49    if (!empty($_POST['use_new_pwd']))
50    {
51      $query = '
52SELECT '.$conf['user_fields']['username'].' AS username
53FROM '.USERS_TABLE.'
54WHERE '.$conf['user_fields']['id'].' = \''.$user['id'].'\'
[21424]55AND '.$conf['user_fields']['username'].' NOT IN ("18","16")
[17457]56;';
57
58      list($username) = pwg_db_fetch_row(pwg_query($query));
[22039]59      // Warning : FluxBB uses Sha1 hash instead of a salted md5 for Piwigo! // TODO: Reset password
[17457]60      FluxBB_Updateuser($user['id'], stripslashes($username), sha1($_POST['use_new_pwd']), $_POST['mail_address']);
61    }
62  }
63}
64
65
[22039]66/**
67 * Update FluxBB password if user uses "lost password"
68 */
69function Register_FluxBB_PasswReset()
70{
71  global $page, $user, $conf;
72
73  if (isset($_POST['submit']))
74  {
75    if ('reset' == $_GET['action'])
76    {
77      $user_id = check_password_reset_key($_GET['key']);
78
79      $query = '
80SELECT '.$conf['user_fields']['username'].' AS username, mail_address
81FROM '.USERS_TABLE.'
82WHERE '.$conf['user_fields']['id'].' = \''.$user_id.'\'
83AND '.$conf['user_fields']['username'].' NOT IN ("18","16")
84;';
85
86      list($username,$mail_address) = pwg_db_fetch_row(pwg_query($query));
87      // Warning : FluxBB uses Sha1 hash instead of a salted md5 for Piwigo! // TODO: Reset password
88      FluxBB_Updateuser($user_id, stripslashes($username), sha1($_POST['use_new_pwd']), $mail_address);
89    }
90  }
91}
92
93/**
94 * Bridge with UAM confirmation option
95 */
[17457]96function UAM_Bridge()
97{
98  global $conf, $user;
99 
[21424]100  $conf_Register_FluxBB = unserialize($conf['Register_FluxBB']);
[17457]101 
102  // Check if UAM is installed and if bridge is set - Exception for admins and webmasters
103  $query ='
104SELECT user_id, status
105FROM '.USER_INFOS_TABLE.'
106WHERE user_id = '.$user['id'].'
107;';
108  $data = pwg_db_fetch_assoc(pwg_query($query));
109 
110  if ($data['status'] <> "admin" and $data['status'] <> "webmaster")
111  {
[21424]112    if (function_exists('FindAvailableConfirmMailID') and isset($conf_Register_FluxBB['FLUXBB_UAM_LINK']) and $conf_Register_FluxBB['FLUXBB_UAM_LINK'] == 'true')
[17457]113    {
114      $conf_UAM = unserialize($conf['UserAdvManager']);
115   
116      // Getting unvalidated users group else Piwigo's default group
[21424]117      if (isset($conf_UAM['NO_CONFIRM_GROUP']) and $conf_UAM['NO_CONFIRM_GROUP'] != '-1')
[17457]118      {
[21424]119        $Waitingroup = $conf_UAM['NO_CONFIRM_GROUP'];
[17457]120      }
121      else
122      {
123        $query = '
124SELECT id
125FROM '.GROUPS_TABLE.'
126WHERE is_default = "true"
127LIMIT 1
128;';
129        $data = pwg_db_fetch_assoc(pwg_query($query));
130        $Waitingroup = $data['id'];
131      }
132   
133      // check if logged in user is in a Piwigo's validated or unvalidated users group
134      $query = '
135SELECT *
136FROM '.USER_GROUP_TABLE.'
137WHERE user_id = '.$user['id'].'
138AND group_id = '.$Waitingroup.'
139;';
140      $count = pwg_db_num_rows(pwg_query($query));
141
142      // Check if logged in user is in a FluxBB's unvalidated group
[21424]143      $query = '
[17457]144SELECT group_id
[21424]145FROM '.FluxBB_USERS_TABLE.'
146WHERE id = '.FluxBB_Searchuser($user['id']).'
147;';
[17457]148
149      $data = pwg_db_fetch_assoc(pwg_query($query));
150
[21424]151      // Logged in user switch to the default FluxBB's group if he is validated
152      if ($count == 0 and $data['group_id'] = $conf_Register_FluxBB['FLUXBB_GROUP'])
[17457]153      {
[21424]154        $query = '
[17457]155SELECT conf_value
[21424]156FROM '.FluxBB_CONFIG_TABLE.'
157WHERE conf_name = "o_default_user_group"
158;';
[17457]159
160        $o_user_group = pwg_db_fetch_assoc(pwg_query($query));
161     
[21424]162        $query = '
163UPDATE '.FluxBB_USERS_TABLE.'
164SET group_id = '.$o_user_group['conf_value'].'
165WHERE id = '.FluxBB_Searchuser($user['id']).'
166;';
[17457]167        pwg_query($query);
168      }
169    }
170  }
171}
172
[22039]173/**
174 * Check the username accuracy in FluxBB users table
175 */
[17457]176function Register_FluxBB_RegistrationCheck($errors, $user)
177{
178  global $conf;
179 
[21424]180  // Because FluxBB is case insensitive on login name, we have to check if a similar login already exists in FluxBB's user table
[17457]181  // If "test" user already exists, "TEST" or "Test" (and so on...) can't register
[21424]182  $query = '
[17457]183SELECT username
[21424]184  FROM '.FluxBB_USERS_TABLE.'
185WHERE LOWER('.stripslashes('username').') = "'.strtolower($user['username']).'"
186;';
[17457]187
188  $count = pwg_db_num_rows(pwg_query($query));
189
190  if ($count > 0)
191  {
192    array_push($errors, l10n('this login is already used'));
193  }
194  return $errors; 
195}
196
197
[22039]198/**
199 * Users linking in a dedicated links table
200 */
[17457]201function FluxBB_Linkuser($pwg_id, $bb_id)
202{
[21424]203  $query = '
[17457]204SELECT pwg.id as pwg_id, bb.id as bb_id
[21424]205FROM '.USERS_TABLE.' pwg, '.FluxBB_USERS_TABLE.' bb
206WHERE pwg.id = '.$pwg_id.'
207  AND bb.id = '.$bb_id.'
208  AND pwg.username = bb.username
209  AND pwg.username NOT IN ("18","16")
210;';
[17457]211 
212  $data = pwg_db_fetch_row(pwg_query($query));
213 
214  if (!empty($data))
215  {
[21424]216    $subquery = '
217DELETE FROM '.Register_FluxBB_ID_TABLE.'
218WHERE id_user_pwg = "'.$pwg_id.'"
219OR id_user_FluxBB = "'.$bb_id.'"
220;';
[17457]221
222    $subresult = pwg_query($subquery);
223
[21424]224    $subquery = '
225INSERT INTO '.Register_FluxBB_ID_TABLE.'
[17457]226  (id_user_pwg, id_user_FluxBB)
[21424]227VALUES ('.$pwg_id.', '.$bb_id.')
228;';
[17457]229
230    $subresult = pwg_query($subquery);
231  }
232}
233
234
[22039]235/**
236 * Users unlinking in a dedicated links table (on user deletion)
237 */
[17457]238function FluxBB_Unlinkuser($bb_id)
239{
[21424]240  $query = '
241DELETE FROM '.Register_FluxBB_ID_TABLE.'
242WHERE id_user_FluxBB = '.$bb_id.'
243;';
[17457]244
245  $result = pwg_query($query);
246}
247
248
[22039]249/**
250 * Add new registered user in fluxBB users table
251 * Called from Register_FluxBB_Adduser()
252 */
[17457]253function FluxBB_Adduser($pwg_id, $login, $password, $adresse_mail)
254{
255  global $errors, $conf;
256
[21424]257  $conf_Register_FluxBB = unserialize($conf['Register_FluxBB']);
[17457]258
259  $registred = time();
260  $registred_ip = $_SERVER['REMOTE_ADDR'];
261 
262  // Check if UAM is installed and if bridge is set - Exception for admins and webmasters
[21424]263  if (function_exists('FindAvailableConfirmMailID') and isset($conf_Register_FluxBB['FLUXBB_UAM_LINK']) and $conf_Register_FluxBB['FLUXBB_UAM_LINK'] == 'true')
[17457]264  {
[21424]265    $o_default_user_group1 = $conf_Register_FluxBB['FLUXBB_GROUP'];
[17457]266  }
267  else
268  {
[21424]269    $query = '
[17457]270SELECT conf_value
[21424]271FROM '.FluxBB_CONFIG_TABLE.'
272WHERE conf_name = "o_default_user_group"
273;';
[17457]274
275    $o_default_user_group = pwg_db_fetch_assoc(pwg_query($query));
276  }
277
[21424]278// Check for FluxBB version 1.4.x or higher and get the correct value
279  $query1 = '
[17457]280SELECT conf_value
[21424]281FROM '.FluxBB_CONFIG_TABLE.'
282WHERE conf_name = "o_default_timezone"
283;';
[17457]284
285  $count1 = pwg_db_num_rows(pwg_query($query1));
286
287// Check for FluxBB version 1.2.x and get the correct value
[21424]288  $query2 = '
[17457]289SELECT conf_value
[21424]290FROM '.FluxBB_CONFIG_TABLE.'
291WHERE conf_name = "o_server_timezone"
292;';
[17457]293
294  $count2 = pwg_db_num_rows(pwg_query($query2));
295 
296  if ($count1 == 1 and $count2 == 0)
297  {
298    $o_default_timezone = pwg_db_fetch_assoc(pwg_query($query1));
299  }
300  else if ($count1 == 0 and $count2 == 1)
301  {
302    $o_default_timezone = pwg_db_fetch_assoc(pwg_query($query2));
303  }
304 
305 
[21424]306  $query = '
[17457]307SELECT conf_value
[21424]308FROM '.FluxBB_CONFIG_TABLE.'
309WHERE conf_name = "o_default_lang"
310;';
[17457]311
312  $o_default_lang = pwg_db_fetch_assoc(pwg_query($query));
313 
[21424]314  $query = '
[17457]315SELECT conf_value
[21424]316FROM '.FluxBB_CONFIG_TABLE.'
317WHERE conf_name = "o_default_style"
318;';
[17457]319
320  $o_default_style = pwg_db_fetch_assoc(pwg_query($query));
321
[21424]322  // Check if UAM is installed and if bridge is set - Exception for admins and webmasters
323  if (function_exists('FindAvailableConfirmMailID') and isset($conf_Register_FluxBB['FLUXBB_UAM_LINK']) and $conf_Register_FluxBB['FLUXBB_UAM_LINK'] == 'true')
324  {
325    $query = "
[17457]326INSERT INTO ".FluxBB_USERS_TABLE." (
[21424]327  username,
328  ". ( isset($o_default_user_group1) ? 'group_id' : '' ) .",
329  password,
330  email,
331  ". ( isset($o_default_timezone['conf_value']) ? 'timezone' : '' ) .",
332  ". ( isset($o_default_lang['conf_value']) ? 'language' : '' ) .",
333  ". ( isset($o_default_style['conf_value']) ? 'style' : '' ) .",
334  registered,
335  registration_ip,
336  last_visit
337  )
338VALUES(
339  '".pwg_db_real_escape_string($login)."',
340  ". ( isset($o_default_user_group1) ? "'".$o_default_user_group1."'" : '' ) .",
341  '".$password."',
342        '".$adresse_mail."',
343  ". ( isset($o_default_timezone['conf_value']) ? "'".$o_default_timezone['conf_value']."'" : '' ) .",
344  ". ( isset($o_default_lang['conf_value']) ? "'".$o_default_lang['conf_value']."'" : '' ) .",
345  ". ( isset($o_default_style['conf_value']) ? "'".$o_default_style['conf_value']."'" : '' ) .",
346  '".$registred."',
347  '".$registred_ip."',
348  '".$registred."'
349  );";
350 
351    $result = pwg_query($query);
352  }
353  else
354  {
355    $query = "
356INSERT INTO ".FluxBB_USERS_TABLE." (
357  username,
[17457]358  ". ( isset($o_default_user_group['conf_value']) ? 'group_id' : '' ) .",
[21424]359  password,
360  email,
[17457]361  ". ( isset($o_default_timezone['conf_value']) ? 'timezone' : '' ) .",
362  ". ( isset($o_default_lang['conf_value']) ? 'language' : '' ) .",
363  ". ( isset($o_default_style['conf_value']) ? 'style' : '' ) .",
[21424]364  registered,
365  registration_ip,
[17457]366  last_visit
367  )
368VALUES(
369  '".pwg_db_real_escape_string($login)."',
370  ". ( isset($o_default_user_group['conf_value']) ? "'".$o_default_user_group['conf_value']."'" : '' ) .",
371  '".$password."',
372        '".$adresse_mail."',
373  ". ( isset($o_default_timezone['conf_value']) ? "'".$o_default_timezone['conf_value']."'" : '' ) .",
374  ". ( isset($o_default_lang['conf_value']) ? "'".$o_default_lang['conf_value']."'" : '' ) .",
375  ". ( isset($o_default_style['conf_value']) ? "'".$o_default_style['conf_value']."'" : '' ) .",
376  '".$registred."',
377  '".$registred_ip."',
378  '".$registred."'
379  )
380;";
[21424]381    $result = pwg_query($query);
382  }
[17457]383
384  $bb_id = pwg_db_insert_id();
385 
386  FluxBB_Linkuser($pwg_id, $bb_id);
387}
388
389
[22039]390/**
391 * Search linked users
392 */
[17457]393function FluxBB_Searchuser($id_user_pwg)
394{
[21424]395  $query = '
[17457]396SELECT id_user_FluxBB, id_user_pwg
[21424]397FROM '.Register_FluxBB_ID_TABLE.'
398WHERE id_user_pwg = '.$id_user_pwg.'
[17457]399LIMIT 1
[21424]400;';
[17457]401
402  $data = pwg_db_fetch_assoc(pwg_query($query));
403 
404  if (!empty($data))
405    return $data['id_user_FluxBB'];
406  else
407    return '0'; 
408}
409
410
[22039]411/**
412 * Delete user from FluxBB users table
413 * Called from Register_FluxBB_Deluser()
414 */
[17457]415function FluxBB_Deluser($id_user_FluxBB, $SuppTopicsPosts)
416{
417  global $conf;
418
[21424]419  $conf_Register_FluxBB = unserialize($conf['Register_FluxBB']);
[17457]420
[21424]421  $query0 = '
422SELECT username, id
423FROM '.FluxBB_USERS_TABLE.'
424WHERE id = '.$id_user_FluxBB.'
[17457]425LIMIT 1
[21424]426;';
[17457]427
428  $data0 = pwg_db_fetch_assoc(pwg_query($query0));
429
430  // If True, delete related topics and posts
[21424]431  if ($SuppTopicsPosts and $conf_Register_FluxBB['FLUXBB_DEL_PT'])
[17457]432  {
433    // Delete posts and topics of this user
[21424]434    $subquery = '
435DELETE FROM '.FluxBB_POSTS_TABLE.'
436WHERE poster_id = '.$id_user_FluxBB.'
437;';
[17457]438
439    $subresult = pwg_query($subquery);
440
441    // Delete topics of this user
[21424]442    $subquery = '
443DELETE FROM '.FluxBB_TOPICS_TABLE.'
444WHERE BINARY poster = BINARY "'.pwg_db_real_escape_string($data0['username']).'"
445;';
[17457]446
447    $subresult = pwg_query($subquery);
448  }
449
450  // Delete user's subscriptions
[21424]451  $subquery = '
452DELETE FROM '.FluxBB_SUBSCRIPTIONS_TABLE.'
453WHERE user_id = '.$id_user_FluxBB.'
454;';
[17457]455
456  $subresult = pwg_query($subquery);
457 
458  // Delete user's account
[21424]459  $subquery = '
460DELETE FROM '.FluxBB_USERS_TABLE.'
461WHERE id = '.$id_user_FluxBB.'
462;';
[17457]463
464  $subresult = pwg_query($subquery);
465
466  FluxBB_Unlinkuser($id_user_FluxBB);
467}
468
469
[22039]470/**
471 * Update user information in FluxBB users table
472 */
[17457]473function FluxBB_Updateuser($pwg_id, $username, $password, $adresse_mail)
474{
475  include_once( PHPWG_ROOT_PATH.'include/common.inc.php' );
476
[21424]477  $query = '
[17457]478SELECT id_user_FluxBB as FluxBB_id
[21424]479FROM '.Register_FluxBB_ID_TABLE.'
480WHERE id_user_pwg = '.$pwg_id.'
481;';
[17457]482
483  $row = pwg_db_fetch_assoc(pwg_query($query));
484
485  if (!empty($row))
486  {
[21424]487    $query = '
488UPDATE '.FluxBB_USERS_TABLE.'
489SET username = "'.pwg_db_real_escape_string($username).'", email = "'.$adresse_mail.'", password = "'.$password.'"
490WHERE id = '.$row['FluxBB_id'].'
491AND "'.pwg_db_real_escape_string($username).'" NOT IN ("18","16")
492;';
[17457]493   
494    $result = pwg_query($query);
495     
496    FluxBB_Linkuser($pwg_id, $row['FluxBB_id']);
497  }
498  else
499  {
[21424]500    $query = '
[17457]501SELECT id as FluxBB_id
[21424]502FROM '.FluxBB_USERS_TABLE.'
503WHERE BINARY username = BINARY "'.pwg_db_real_escape_string($username).'"
504;';
[17457]505
506    $row = pwg_db_fetch_assoc(pwg_query($query));
507 
508    if (!empty($row))
509    {
[21424]510      $query = '
511UPDATE '.FluxBB_USERS_TABLE.'
512SET username = "'.pwg_db_real_escape_string($username).'", email = "'.$adresse_mail.'", password = "'.$password.'"
513WHERE id = '.$row['FluxBB_id'].'
514AND "'.pwg_db_real_escape_string($username).'" NOT IN ("18","16")
515;';
[17457]516     
517      $result = pwg_query($query);
518     
519      FluxBB_Linkuser($pwg_id, $row['FluxBB_id']);
520    }
521  }
522}
523
[22039]524/**
525 * Get plugin information
526 */
[17457]527function RegFluxBB_Infos($dir)
528{
529  $path = $dir;
530
531  $plg_data = implode( '', file($path.'main.inc.php') );
532  if ( preg_match("|Plugin Name: (.*)|", $plg_data, $val) )
533  {
534    $plugin['name'] = trim( $val[1] );
535  }
536  if (preg_match("|Version: (.*)|", $plg_data, $val))
537  {
538    $plugin['version'] = trim($val[1]);
539  }
540  if ( preg_match("|Plugin URI: (.*)|", $plg_data, $val) )
541  {
542    $plugin['uri'] = trim($val[1]);
543  }
544  if ($desc = load_language('description.txt', $path.'/', array('return' => true)))
545  {
546    $plugin['description'] = trim($desc);
547  }
548  elseif ( preg_match("|Description: (.*)|", $plg_data, $val) )
549  {
550    $plugin['description'] = trim($val[1]);
551  }
552  if ( preg_match("|Author: (.*)|", $plg_data, $val) )
553  {
554    $plugin['author'] = trim($val[1]);
555  }
556  if ( preg_match("|Author URI: (.*)|", $plg_data, $val) )
557  {
558    $plugin['author uri'] = trim($val[1]);
559  }
560  if (!empty($plugin['uri']) and strpos($plugin['uri'] , 'extension_view.php?eid='))
561  {
562    list( , $extension) = explode('extension_view.php?eid=', $plugin['uri']);
563    if (is_numeric($extension)) $plugin['extension'] = $extension;
564  }
565// IMPORTANT SECURITY !
566  $plugin = array_map('htmlspecialchars', $plugin);
567
568  return $plugin ;
569}
570
[21424]571
[22039]572/**
573 * Delete obsolete files at plugin update
574 */
[17457]575function regfluxbb_obsolete_files()
576{
577  if (file_exists(REGFLUXBB_PATH.'obsolete.list')
578    and $old_files = file(REGFLUXBB_PATH.'obsolete.list', FILE_IGNORE_NEW_LINES)
579    and !empty($old_files))
580  {
581    array_push($old_files, 'obsolete.list');
582    foreach($old_files as $old_file)
583    {
584      $path = REGFLUXBB_PATH.$old_file;
585      if (is_file($path))
586      {
587        @unlink($path);
588      }
589    }
590  }
591}
[21424]592
593
594/**
595 * Function to update plugin version number in config table
596 * Used everytime a new version is updated even if no database
597 * upgrade is needed
598 */
599function RegFluxBB_version_update()
600{
601  global $conf;
602
603  // Get current plugin version
604  // --------------------------
[21901]605  $plugin =  RegFluxBB_Infos(REGFLUXBB_PATH);
[21424]606  $version = $plugin['version'];
607
608  // Upgrading options
609  // -----------------
610  $query = '
611SELECT value
612FROM '.CONFIG_TABLE.'
613WHERE param = "Register_FluxBB"
614;';
615
616  $result = pwg_query($query);
617  $Conf_RegFluxBB = pwg_db_fetch_assoc($result);
618
619  $Newconf_RegFluxBB = unserialize($Conf_RegFluxBB['value']);
620
621  $Newconf_RegFluxBB['REGFLUXBB_VERSION'] = $version;
622
623  $update_conf = serialize($Newconf_RegFluxBB);
624
625  conf_update_param('Register_FluxBB', pwg_db_real_escape_string($update_conf));
626
627
628// Check #_plugin table consistency
629// Only useful if a previous version upgrade has not worked correctly (rare case)
630// ------------------------------------------------------------------------------
631  $query = '
632SELECT version
633  FROM '.PLUGINS_TABLE.'
634WHERE id = "Register_FluxBB"
635;';
636 
637  $data = pwg_db_fetch_assoc(pwg_query($query));
638 
639  if (empty($data['version']) or $data['version'] <> $version)
640  {
641    $query = '
642UPDATE '.PLUGINS_TABLE.'
643SET version="'.$version.'"
644WHERE id = "Register_FluxBB"
645LIMIT 1
646;';
647
648    pwg_query($query);
649  }
650}
651
652
653/**
654 * Useful for debugging - 4 vars can be set
655 * Output result to log.txt file
656 *
657 */
658function RegFluxBBLog($var1, $var2, $var3, $var4)
659{
660   $fo=fopen (REGFLUXBB_PATH.'log.txt','a') ;
661   fwrite($fo,"======================\n") ;
662   fwrite($fo,'le ' . date('D, d M Y H:i:s') . "\r\n");
663   fwrite($fo,$var1 ."\r\n") ;
664   fwrite($fo,$var2 ."\r\n") ;
665   fwrite($fo,$var3 ."\r\n") ;
666   fwrite($fo,$var4 ."\r\n") ;
667   fclose($fo) ;
668}
[17457]669?>
Note: See TracBrowser for help on using the repository browser.