/[drupal]/contributions/modules/versioncontrol_cvs/versioncontrol_cvs.module
ViewVC logotype

Contents of /contributions/modules/versioncontrol_cvs/versioncontrol_cvs.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.47 - (show annotations) (download) (as text)
Wed Apr 8 19:17:26 2009 UTC (7 months, 3 weeks ago) by jpetso
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-RC1, HEAD
Changes since 1.46: +35 -7 lines
File MIME type: text/x-php
Implement export_directory(), and adapt get_item() to the changed API.
1 <?php
2 // $Id: versioncontrol_cvs.module,v 1.46 2009/03/21 00:54:40 jpetso Exp $
3 /**
4 * @file
5 * CVS backend for Version Control API - Provides CVS commit information and
6 * account management as a pluggable backend.
7 *
8 * Copyright 2005 by Kjartan Mannes ("Kjartan", http://drupal.org/user/2)
9 * Copyright 2006, 2007 by Derek Wright ("dww", http://drupal.org/user/46549)
10 * Copyright 2007, 2008, 2009 by Jakob Petsovits ("jpetso", http://drupal.org/user/56020)
11 */
12
13 // Update methods.
14 define('VERSIONCONTROL_CVS_UPDATE_CRON', 0);
15 define('VERSIONCONTROL_CVS_UPDATE_XCVS', 1);
16
17 // The admin and user edit pages.
18 include_once(drupal_get_path('module', 'versioncontrol_cvs') .'/versioncontrol_cvs.admin.inc');
19
20 /**
21 * Implementation of hook_versioncontrol_backends().
22 */
23 function versioncontrol_cvs_versioncontrol_backends() {
24 return array(
25 // The array key is up to 8 characters long, and used as unique identifier
26 // for this VCS, in functions, URLs and in the database.
27 'cvs' => array(
28 // The user-visible name of the VCS.
29 'name' => 'CVS',
30
31 // A short description of the VCS, if possible not longer than one or two sentences.
32 'description' => t('CVS (Concurrent Versions System) is a slightly older code management system that supports file revisions, tags and branches, but lacks atomic commits, advanced merge functionality and support for renaming items.'),
33
34 // A list of optional capabilities, in addition to the required retrieval
35 // of detailed commit information.
36 'capabilities' => array(
37 // Able to cancel commits if the committer lacks permissions
38 // to commit to specific paths and/or branches.
39 VERSIONCONTROL_CAPABILITY_COMMIT_RESTRICTIONS,
40 // Able to cancel branch or tag assignments if the committer lacks
41 // permissions to create/update/delete those.
42 VERSIONCONTROL_CAPABILITY_BRANCH_TAG_RESTRICTIONS,
43 ),
44
45 // An array listing which tables should be managed by Version Control API
46 // instead of doing it manually in the backend.
47 'flags' => array(
48 // versioncontrol_insert_repository() will automatically insert
49 // array elements from $repository['cvs_specific'] into
50 // {versioncontrol_cvs_repositories} and versioncontrol_get_repositories()
51 // will automatically fetch it from there.
52 VERSIONCONTROL_FLAG_AUTOADD_REPOSITORIES,
53 ),
54 ),
55 );
56 }
57
58 /**
59 * Implementation of hook_menu().
60 */
61 function versioncontrol_cvs_menu() {
62 global $user;
63 $items = array();
64
65 $items['admin/project/versioncontrol-repositories/update/cvs/%versioncontrol_repository'] = array(
66 'title' => 'Fetch log',
67 'page callback' => 'versioncontrol_cvs_update_repository_callback',
68 'page arguments' => array(5),
69 'access callback' => 'versioncontrol_admin_access',
70 'type' => MENU_CALLBACK,
71 );
72 return $items;
73 }
74
75 /**
76 * Implementation of hook_cron():
77 * Update repositories that have log fetching enabled.
78 */
79 function versioncontrol_cvs_cron() {
80 $result = db_query("SELECT repo_id FROM {versioncontrol_cvs_repositories}
81 WHERE update_method = %d", VERSIONCONTROL_CVS_UPDATE_CRON);
82
83 // Set timeout limit to 3600 seconds as it can take a long time to process
84 // the log initially. (And hook_cron() might be called by poormanscron.)
85 if (!ini_get('safe_mode')) {
86 set_time_limit(3600);
87 }
88 while ($repo = db_fetch_object($result)) {
89 $repository = versioncontrol_get_repository($repo->repo_id);
90 if (isset($repository)) {
91 _versioncontrol_cvs_update_repository($repository);
92 }
93 }
94 }
95
96 /**
97 * Include the cvslib.inc helper library so that its functions can be called.
98 */
99 function _versioncontrol_cvs_init_cvslib() {
100 include_once(drupal_get_path('module', 'versioncontrol_cvs') .'/cvslib/cvslib.inc');
101 }
102
103
104 /**
105 * Implementation of [versioncontrol_backend]_get_item():
106 * Try to retrieve a given item in a repository.
107 */
108 function versioncontrol_cvs_get_item($repository, $path, $constraints = array()) {
109 _versioncontrol_cvs_init_cvslib();
110
111 $label = !empty($constraints['label']) ? $constraints['label'] : array(
112 'name' => 'HEAD',
113 'type' => VERSIONCONTROL_OPERATION_BRANCH,
114 );
115 $directory_item = array(
116 'path' => $path,
117 'revision' => '',
118 'type' => VERSIONCONTROL_ITEM_DIRECTORY,
119 );
120
121 // Special casing the root directory, because it's always the same.
122 if ($path == '/') {
123 return array('item' => $directory_item, 'selected_label' => $label);
124 }
125
126 // Use ls to see whether the item is a directory.
127 $ls_constraints = array(
128 'show_dead_files' => TRUE,
129 'show_empty_dirs' => TRUE,
130 );
131 if (!empty($constraints['revision'])) {
132 $ls_constraints['revision'] = $constraints['revision'];
133 }
134 if (!isset($ls_constraints['revision'])) {
135 $ls_constraints['revision'] = $label['name'];
136 }
137 if (!empty($label['date'])) {
138 $ls_constraints['date'] = $label['date'];
139 }
140 $contents = cvslib_ls($repository['root'], dirname($path), $ls_constraints);
141
142 if (!isset($contents[$path])) {
143 return NULL; // does not exist (at least not at the given revision)
144 }
145 else if ($contents[$path]->directory) {
146 return array('item' => $directory_item, 'selected_label' => $label);
147 }
148 else {
149 // The item exists and is a file. We want more info (dead or not, label)
150 // so let's retrieve further info with cvs log.
151 $file_revisions = cvslib_log($repository['root'], $path, array(
152 'revision' => $contents[$path]->revision,
153 'recursive' => FALSE,
154 'show_dead_files' => TRUE,
155 ));
156 if ($file_revisions === FALSE || empty($file_revisions)) {
157 return NULL;
158 }
159 $file_revision = reset($file_revisions); // first array element
160
161 if ($file_revision->path != $path) {
162 // Should not happen, but let's avoid subtle bugs and race conditions.
163 return NULL;
164 }
165
166 $item = array(
167 'path' => $file_revision->path,
168 'revision' => $file_revision->revision,
169 'type' => $file_revision->dead
170 ? VERSIONCONTROL_ITEM_FILE_DELETED
171 : VERSIONCONTROL_ITEM_FILE,
172 );
173 if (!empty($constraints['label'])) {
174 $label = $constraints['label'];
175 }
176 elseif (empty($file_revision->branch)) {
177 $label = NULL; // should not happen except in unknown edge cases
178 }
179 else {
180 $label['name'] = $file_revision->branch;
181 }
182 return array('item' => $item, 'selected_label' => $label);
183 }
184 }
185
186 /**
187 * Implementation of [versioncontrol_backend]_get_parallel_items():
188 * Given an item in a repository, retrieve related versions of that item on all
189 * different branches and/or tags where the item exists.
190 */
191 function versioncontrol_cvs_get_parallel_items($repository, $item, $label_type_filter = NULL) {
192 _versioncontrol_cvs_init_cvslib();
193
194 $file_revisions = cvslib_log($repository['root'], $item['path'], array(
195 // Fetching only the latest HEAD version still retrieves all labels.
196 'revision' => 'HEAD.',
197 // If the item is a directory, only consider its direct children.
198 // (Think of querying d.o's "/contributions" recursively. ... ...right.)
199 'recursive' => FALSE,
200 ));
201 if ($file_revisions === FALSE) {
202 return NULL;
203 }
204 $labels = array(
205 VERSIONCONTROL_OPERATION_BRANCH => array('HEAD'),
206 VERSIONCONTROL_OPERATION_TAG => array(),
207 );
208 $is_directory_item = TRUE;
209
210 foreach ($file_revisions as $file_revision) {
211 // For directory items, the file children will be retrieved, so we can tell
212 // by comparing the given and the resulting path which kind of item it is.
213 if ($is_directory_item && $item['path'] == $file_revision->path) {
214 $is_directory_item = FALSE;
215 }
216 $labels[VERSIONCONTROL_OPERATION_BRANCH] = array_unique(array_merge(
217 $labels[VERSIONCONTROL_OPERATION_BRANCH], $file_revision->all_branches
218 ));
219 $labels[VERSIONCONTROL_OPERATION_TAG] = array_unique(array_merge(
220 $labels[VERSIONCONTROL_OPERATION_TAG], $file_revision->all_tags
221 ));
222 }
223 if (isset($label_type_filter)) {
224 unset($labels[$label_type_filter]);
225 }
226
227 // Construct the list of parallel items.
228 // Slightly different depending on the item type.
229 if ($is_directory_item) {
230 $parallel_item = array(
231 'path' => $item['path'],
232 'revision' => '',
233 'type' => VERSIONCONTROL_ITEM_DIRECTORY,
234 );
235 }
236 $parallel_items = array();
237
238 foreach ($labels as $label_type => $label_names) {
239 foreach ($label_names as $label_name) {
240 if (!$is_directory_item) {
241 $parallel_item = _versioncontrol_cvs_file_item_from_cvslib_ls(
242 $repository, $item['path'], $label_name
243 );
244 if (!isset($parallel_item)) {
245 continue; // should not happen expect in unknown edge cases
246 }
247 }
248 $label = array(
249 'type' => $label_type,
250 'name' => $label_name,
251 );
252 $parallel_items[] = array('item' => $parallel_item, 'selected_label' => $label);
253 }
254 }
255 return $parallel_items;
256 }
257
258 /**
259 * Create a Version Control API file item by calling cvslib_ls() on a path and
260 * revision that is known to exist. The item label cannot reliably retrieved
261 * with cvslib_ls(), so you need to call cvslib_log() on the specific file
262 * revision if you want that too.
263 *
264 * @return
265 * An item array if the file could be retrieved, or NULL in case of an error.
266 */
267 function _versioncontrol_cvs_file_item_from_cvslib_ls($repository, $path, $revision = NULL) {
268 $constraints = array(
269 'revision' => empty($revision) ? 'HEAD' : $revision,
270 'recursive' => FALSE,
271 'show_dead_files' => FALSE,
272 );
273 $file_is_dead = FALSE;
274 $file_revisions = cvslib_ls($repository['root'], $path, $constraints);
275
276 if (is_array($file_revisions) && empty($file_revisions)) {
277 $constraints['show_dead_files'] = TRUE;
278 $file_is_dead = TRUE;
279 $file_revisions = cvslib_ls($repository['root'], $path, $constraints);
280 }
281 if ($file_revisions === FALSE || empty($file_revisions)) {
282 return NULL;
283 }
284 $file_revision = reset($file_revisions); // first array element
285
286 if ($file_revision->path != $path) {
287 // Should not happen, but let's avoid subtle bugs and race conditions.
288 return NULL;
289 }
290
291 return array(
292 'path' => $file_revision->path,
293 'revision' => $file_revision->revision,
294 'type' => $file_is_dead
295 ? VERSIONCONTROL_ITEM_FILE_DELETED
296 : VERSIONCONTROL_ITEM_FILE,
297 );
298 }
299
300 /**
301 * Implementation of [versioncontrol_backend]_get_directory_contents():
302 * Retrieve the set of files and directories that exist at a specified revision
303 * in the given directory inside the repository.
304 */
305 function versioncontrol_cvs_get_directory_contents($repository, $directory_item, $recursive = FALSE) {
306 _versioncontrol_cvs_init_cvslib();
307
308 $selected_label = versioncontrol_get_item_selected_label($repository, $directory_item);
309 $label_name = empty($selected_label) ? 'HEAD' : $selected_label['name'];
310
311 $contents = cvslib_ls($repository['root'], $directory_item['path'], array(
312 'revision' => $label_name,
313 'recursive' => $recursive,
314 ));
315 if ($contents === FALSE) {
316 return NULL;
317 }
318 $items = array();
319
320 foreach ($contents as $path => $item_info) {
321 $items[$path] = array(
322 'item' => array(
323 'path' => $item_info->path,
324 'revision' => ($item_info->directory) ? '' : $item_info->revision,
325 'type' => ($item_info->directory)
326 ? VERSIONCONTROL_ITEM_DIRECTORY
327 : VERSIONCONTROL_ITEM_FILE,
328 ),
329 'selected_label' => array(
330 'type' => empty($selected_label)
331 ? VERSIONCONTROL_OPERATION_BRANCH
332 : $selected_label['type'],
333 'name' => $label_name,
334 ),
335 );
336 }
337 return $items;
338 }
339
340 /**
341 * Implementation of [versioncontrol_backend]_export_file():
342 * Retrieve a copy of the contents of a given item in the repository.
343 */
344 function versioncontrol_cvs_export_file($repository, $file_item, $destination) {
345 _versioncontrol_cvs_init_cvslib();
346
347 $success = cvslib_cat($destination, $repository['root'], $file_item['path'],
348 array('revision' => $file_item['revision']));
349 return $success;
350 }
351
352 /**
353 * Implementation of [versioncontrol_backend]_export_directory():
354 * Retrieve a copy of the given directory item in the repository.
355 */
356 function versioncontrol_cvs_export_directory($repository, $directory_item, $destination_dirpath) {
357 _versioncontrol_cvs_init_cvslib();
358
359 $selected_label = versioncontrol_get_item_selected_label($repository, $directory_item);
360 $constraints = array(
361 'revision' => empty($selected_label) ? 'HEAD' : $selected_label['name'],
362 );
363 if (!empty($selected_label['date'])) {
364 $constraints['date'] = $selected_label['date'];
365 }
366
367 $success = cvslib_export($destination_dirpath, $repository['root'],
368 $directory_item['path'], $constraints);
369 return $success;
370 }
371
372 /**
373 * Implementation of [versioncontrol_backend]_get_selected_label_from_operation():
374 * Retrieve the tag or branch that applied to that item during the given
375 * operation. The result of this function will be used for the selected label
376 * property of each item, which is necessary to provide a starting point for
377 * branch and tag navigation.
378 */
379 function versioncontrol_cvs_get_selected_label_from_operation($operation, $target_item) {
380 // Tag renames are represented by two labels, "added" and "deleted".)
381 // In that case, make sure that the "added" label will be selected.
382 if (count($operation['labels'] > 1)) {
383 foreach ($operation['labels'] as $label) {
384 if ($label['action'] == VERSIONCONTROL_ACTION_ADDED) {
385 return $label;
386 }
387 }
388 }
389 // Otherwise, each operation by the CVS backend has exactly one branch or tag
390 // assigned, so we can just return that one for all items in any operation.
391 // NOTE: That might change though, in case the CVS backend gets functionality
392 // to add branches to commits later - because when another branch is
393 // forked off, all previous commits to a file are now both in the
394 // original branch *and* in the new branch. Very low priority, though.
395 return $operation['labels'][0];
396 }
397
398 /**
399 * Implementation of [versioncontrol_backend]_get_selected_label_from_other_item():
400 * Retrieve a valid label (tag or branch) for a new @p $target_item that is
401 * (hopefully) similar or related to that of the given @p $other_item which
402 * already has a selected label assigned. If the backend cannot find a related
403 * label, return any valid label. The result of this function will be used for
404 * the selected label property of each item, which is necessary to preserve the
405 * item state throughout navigational API functions.
406 */
407 function versioncontrol_cvs_get_selected_label_from_other_item($repository, $target_item, &$other_item, $other_item_tags = array()) {
408 // First up, optimizations - maybe we can do without the generic
409 // "label transfer" code from further down and use assumptions instead.
410
411 // A directory can always have the same tag name as another item, in the
412 // worst case a directory content listing just won't list any files.
413 if (versioncontrol_is_directory_item($target_item)) {
414 return versioncontrol_get_item_selected_label($repository, $other_item);
415 }
416
417 // Each source item is also available on the same branch as its successor.
418 // That means we can take the label as is - if it's actually a branch.
419 if ($other_item == 'successor_item') {
420 $label = versioncontrol_get_item_selected_label($repository, $other_item);
421
422 if (isset($label['type']) && $label['type'] == VERSIONCONTROL_OPERATION_BRANCH) {
423 return $label;
424 }
425 }
426
427 // Otherwise we can't really figure out a label for this item -
428 // it's a tag label array or NULL - so we either have to query CVS itself
429 // (most correct solution except when invoking CVS fails for any reason),
430 // or look up in the database if we know the commit associated to this item
431 // revision (correct solution if the commit has been recorded and the
432 // database has captured all branch/tag renames correctly). In case both
433 // of those don't work, we can only return NULL - which is not really a nice
434 // thing to do, as normally the CVS backend always provides a branch.
435 // But that's how it goes, I guess.
436
437 // Try 1 (more performant than process invocation):
438 // Look if the database contains the associated commit.
439 if (versioncontrol_fetch_item_revision_id($repository, $target_item)) {
440 $constraints = array(
441 'item_revision_id' => array($target_item['item_revision_id']),
442 );
443 $commit_operations = versioncontrol_get_commit_operations($constraints);
444
445 if (!empty($commit_operations)) { // yo, found the associated commit!
446 $commit_operation = reset($commit_operations); // first (only) element
447 return $commit_operation['labels'][0];
448 }
449 }
450
451 // Hm, didn't work, so...
452 // Try 2: Get the information directly from the CVS process.
453 // TODO: implement? or just leave it at that as "good enough"?
454
455 // No label could be retrieved by looking at the other item, sorry.
456 return NULL;
457 }
458
459
460 /**
461 * CVS tells us that the file was modified (has a previous revision) even if
462 * it has been deleted before. Technically that's correct, but we'd like to
463 * have it show up as "added", so this function tries to be smart and alters
464 * an operation items array accordingly if the database tells us that the
465 * previous revision of a file was dead.
466 */
467 function _versioncontrol_cvs_fix_commit_operation_items($operation, &$operation_items) {
468 foreach ($operation_items as $path => $item) {
469 if ($item['action'] != VERSIONCONTROL_ACTION_MODIFIED) {
470 continue;
471 }
472 $repo_id = isset($operation['repository'])
473 ? $operation['repository']['repo_id']
474 : $operation['repo_id'];
475 $count = db_result(db_query(
476 "SELECT COUNT(*)
477 FROM {versioncontrol_item_revisions} ir
478 INNER JOIN {versioncontrol_operation_items} opitem
479 ON ir.item_revision_id = opitem.item_revision_id
480 INNER JOIN {versioncontrol_operations} op
481 ON opitem.vc_op_id = op.vc_op_id
482 WHERE op.repo_id = %d AND op.type = %d
483 AND ir.type = %d AND ir.path = '%s' AND ir.revision = '%s'",
484 $repo_id, VERSIONCONTROL_OPERATION_COMMIT,
485 VERSIONCONTROL_ITEM_FILE_DELETED, $path,
486 $action['source_items'][0]['revision']
487 ));
488 if ($count > 0) {
489 $operation_items[$path]['action'] = VERSIONCONTROL_ACTION_ADDED;
490 $operation_items[$path]['source_items'] = array();
491 }
492 }
493 }
494
495 /**
496 * Implementation of [versioncontrol_backend]_account():
497 * Manage (insert, update or delete) additional CVS user account data
498 * in the database.
499 *
500 * @param $op
501 * Either 'insert' when the account is in the process of being created,
502 * or 'update' when username or additional module data change,
503 * or 'delete' if it will be deleted after this function has been called.
504 * @param $uid
505 * The Drupal user id corresponding to the VCS account.
506 * @param $username
507 * The VCS specific username (a string).
508 * @param $repository
509 * The repository where the user has its VCS account.
510 * @param $additional_data
511 * An array of additional author information.
512 */
513 function versioncontrol_cvs_account($op, $uid, $username, $repository, $additional_data = array()) {
514 $cvs_specific = isset($additional_data['cvs_specific'])
515 ? $additional_data['cvs_specific']
516 : NULL;
517
518 switch ($op) {
519 case 'insert':
520 if (!isset($cvs_specific) || !isset($cvs_specific['password'])) {
521 drupal_set_message(t('Error: no CVS password given on account creation!'), 'error');
522 return;
523 }
524 db_query("INSERT INTO {versioncontrol_cvs_accounts}
525 (uid, repo_id, password)
526 VALUES (%d, %d, '%s')",
527 $uid, $repository['repo_id'], $cvs_specific['password']);
528 break;
529
530 case 'update':
531 if (!isset($cvs_specific) || !isset($cvs_specific['password'])) {
532 return; // the user didn't update the password in the process.
533 }
534 db_query("UPDATE {versioncontrol_cvs_accounts}
535 SET password = '%s'
536 WHERE uid = %d AND repo_id = %d",
537 $cvs_specific['password'], $uid, $repository['repo_id']);
538
539 if (!versioncontrol_admin_access()) {
540 // Admins get "The account has been updated successfully" anyways.
541 drupal_set_message(t('The CVS password has been updated successfully.'));
542 }
543 break;
544
545 case 'delete':
546 db_query('DELETE FROM {versioncontrol_cvs_accounts}
547 WHERE uid = %d AND repo_id = %d',
548 $uid, $repository['repo_id']);
549 break;
550 }
551 }
552
553 /**
554 * Implementation of [vcs_backend]_import_accounts():
555 * Import accounts into a repository, given text data from the accounts file.
556 * No accounts are deleted, new accounts are inserted, and existing accounts
557 * are updated with imported ones.
558 *
559 * @param $repository
560 * The repository where the accounts will be imported.
561 * @param $data
562 * The contents of the "account data" text area where the user has to
563 * enter/copy the contents of the version control system's accounts file.
564 */
565 function versioncontrol_cvs_import_accounts($repository, $data) {
566 $lines = explode("\n", $data);
567 $names = array();
568
569 foreach ($lines as $line) {
570 if (preg_match('/^\s*(#.*)?$/', $line)) { // filter out empty and commented lines
571 continue;
572 }
573 // Extract the account information and create or update the user accounts.
574 list($username, $password, $run_as_user) = explode(':', $line);
575 if (!empty($username) && !empty($password)) {
576 $additional_data = array(
577 'cvs_specific' => array('password' => $password),
578 );
579 $uid = versioncontrol_get_account_uid_for_username($repository['repo_id'], $username, TRUE);
580
581 if (isset($uid)) {
582 versioncontrol_update_account($repository, $uid, $username, $additional_data);
583 $names[] = t('updated !username', array('!username' => $username));
584 }
585 else {
586 $uid = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $username));
587 if ($uid) {
588 versioncontrol_insert_account($repository, $uid, $username, $additional_data);
589 $names[] = t('added !username', array('!username' => $username));
590 }
591 else {
592 $names[] = t('didn\'t add !username (no matching Drupal username exists)',
593 array('!username' => $username));
594 }
595 }
596 }
597 }
598
599 if (empty($names)) {
600 drupal_set_message(t('Failed to import CVS accounts.'), 'error');
601 }
602 else {
603 drupal_set_message(theme('item_list', $names, t('The import of CVS accounts has been completed successfully:')));
604 }
605 }
606
607 /**
608 * Implementation of [vcs_backend]_export_accounts():
609 * Export accounts of a repository to text data that is suitable for
610 * copying to the version control system's accounts file.
611 *
612 * @param $repository
613 * The repository whose accounts will be exported.
614 * @param $accounts
615 * The list (array) of accounts that should be exported, given in the same
616 * format as the return value of versioncontrol_get_accounts().
617 * All accounts in this list are from the above repository.
618 *
619 * @return
620 * The exported textual representation of the account list.
621 */
622 function versioncontrol_cvs_export_accounts($repository, $accounts) {
623 if (empty($accounts)) {
624 return '# '. t('no user accounts available to export');
625 }
626
627 $accounts_flat = array();
628 $uid_constraints = array();
629 $params = array($repository['repo_id']);
630
631 foreach ($accounts as $uid => $usernames_per_repository) {
632 foreach ($usernames_per_repository as $repo_id => $username) {
633 $accounts_flat[$uid] = array('uid' => $uid, 'username' => $username);
634 $uid_constraints[] = 'uid = %d';
635 $params[] = $uid;
636 }
637 }
638
639 $result = db_query('SELECT uid, password FROM {versioncontrol_cvs_accounts}
640 WHERE repo_id = %d
641 AND ('. implode(' OR ', $uid_constraints) .')',
642 $params);
643 while ($account = db_fetch_object($result)) {
644 $accounts_flat[$account->uid]['password'] = $account->password;
645 }
646
647 $run_as_user = '';
648 if (!empty($repository['run_as_user'])) {
649 $run_as_user = ':'. $repository['run_as_user'];
650 }
651 $data = '';
652 foreach ($accounts_flat as $uid => $account) {
653 $data .= '# '. url('user/'. $uid, array('absolute' => TRUE)) ."\n";
654 $data .= $account['username'] .':'. $account['password'] . $run_as_user ."\n\n";
655 }
656 return $data;
657 }
658
659
660 /**
661 * Menu callback for
662 * 'admin/project/versioncontrol-repositories/update/cvs/%versioncontrol_repository':
663 * Retrieve/validate the specified repository, fetch new commits, tags
664 * and branches by invoking the cvs executable, output messages and
665 * redirect back to the repository page.
666 */
667 function versioncontrol_cvs_update_repository_callback($repository) {
668 if ($repository['cvs_specific']['update_method'] == VERSIONCONTROL_CVS_UPDATE_CRON) {
669 // Set timeout limit to 3600 seconds as it can take a long time
670 // to process the log initially.
671 if (!ini_get('safe_mode')) {
672 set_time_limit(3600);
673 }
674 if (_versioncontrol_cvs_update_repository($repository)) {
675 drupal_set_message(t('Fetched new log entries.'));
676 }
677 }
678 else {
679 drupal_set_message(t('Repository update method does not allow manual updates, did not fetch anything.'));
680 }
681 drupal_goto('admin/project/versioncontrol-repositories');
682 }
683
684 /**
685 * Actually update the repository by fetching commits and other stuff
686 * directly from the repository, invoking the cvs executable.
687 *
688 * @return
689 * TRUE if the logs were updated, or FALSE if fetching and updating the logs
690 * failed for whatever reason.
691 */
692 function _versioncontrol_cvs_update_repository(&$repository) {
693 include_once(drupal_get_path('module', 'versioncontrol_cvs') .'/versioncontrol_cvs.log.inc');
694 return _versioncontrol_cvs_log_update_repository($repository);
695 }
696
697 /**
698 * Calculate the previous revision number of file under version control,
699 * given the current revision. This can be done in a purely programmatical way
700 * because of the quite special numbering scheme of CVS (so no database queries
701 * need to be done in order to get the result).
702 *
703 * @return
704 * The previous revision number (e.g. "1.1" for a given "1.2",
705 * or "1.3" for a given "1.3.2.1"), or NULL if $current_revision is "1.1"
706 * (which obviously means that there is no previous revision number).
707 */
708 function versioncontrol_cvs_get_previous_revision_number($current_revision) {
709 if ($current_revision === '1.1') {
710 return NULL;
711 }
712 $parts = explode('.', $current_revision);
713 $last_part = array_pop($parts);
714
715 // For the first commit to a new branch, cut off the two rightmost parts
716 // in order to get the previous revision number. (e.g. "1.3.4.1" is the
717 // first commit on branch 1.3.0.4 which originated in "1.3" from HEAD.)
718 if ($last_part === '1') {
719 array_pop($parts); // e.g., removes the "2" from the example above
720 return implode('.', $parts);
721 }
722
723 // If we don't have a "1" as last part, we can just decrease this by one
724 // and implode it again to get the previous revision.
725 $last_part = ((int) $last_part) - 1;
726 $parts[] = (string) $last_part;
727 return implode('.', $parts);
728 }

  ViewVC Help
Powered by ViewVC 1.1.2