diff --git includes/VersioncontrolRepository.php includes/VersioncontrolRepository.php index 7482a9c..4617c62 100644 --- includes/VersioncontrolRepository.php +++ includes/VersioncontrolRepository.php @@ -99,7 +99,7 @@ abstract class VersioncontrolRepository implements VersioncontrolEntityInterface * The current plugin types(array keys) are: * - author_mapper * - committer_mapper - * - auth_handler + * - webviewer_url_handler * * @var array */ @@ -112,6 +112,13 @@ abstract class VersioncontrolRepository implements VersioncontrolEntityInterface */ protected $pluginInstances = array(); + /** + * The instance for this repository url handler. + * + * @var VersioncontrolRepositoryUrlHandlerInterface + */ + protected $urlHandler; + protected $defaultCrudOptions = array( 'update' => array('nested' => TRUE), 'insert' => array('nested' => TRUE), @@ -366,24 +373,24 @@ abstract class VersioncontrolRepository implements VersioncontrolEntityInterface * Convinience method to retrieve url handler. */ public function getUrlHandler() { - if (!isset($this->data['versioncontrol']['url_handler'])) { - $this->data['versioncontrol']['url_handler'] = - new VersioncontrolRepositoryUrlHandler( - $this, VersioncontrolRepositoryUrlHandler::getEmpty() - ); + if (!isset($this->urlHandler)) { + $plugin = $this->getPlugin('webviewer_url_handler', 'webviewer_url_handlers'); + $class_name = ctools_plugin_get_class($plugin, 'handler'); + if (!class_exists($class_name)) { + throw new Exception("Plugin 'webviewer_url_handler' of type 'webviewer_url_handlers' does not contain a valid class name in handler slot 'handler'", E_WARNING); + return FALSE; + } + $this->urlHandler = new $class_name( + $this, $this->data['versioncontrol']['base_url'], $plugin['url_templates'] + ); } - return $this->data['versioncontrol']['url_handler']; + return $this->urlHandler; } /** - * Get an instantiated plugin object based on a requested plugin slot, and the - * plugin this repository object has assigned to that slot. - * - * Internal function - other methods should provide a nicer public-facing - * interface. This method exists primarily to reduce code duplication involved - * in ensuring error handling and sound loading of the plugin. + * Get a ctools plugin based on plugin slot passed. */ - protected function getPluginClass($plugin_slot, $plugin_type, $class_type) { + protected function getPlugin($plugin_slot, $plugin_type) { ctools_include('plugins'); if (empty($this->plugins[$plugin_slot])) { @@ -398,6 +405,20 @@ abstract class VersioncontrolRepository implements VersioncontrolEntityInterface return FALSE; } + return $plugin; + } + + /** + * Get an instantiated plugin object based on a requested plugin slot, and the + * plugin this repository object has assigned to that slot. + * + * Internal function - other methods should provide a nicer public-facing + * interface. This method exists primarily to reduce code duplication involved + * in ensuring error handling and sound loading of the plugin. + */ + protected function getPluginClass($plugin_slot, $plugin_type, $class_type) { + $plugin = $this->getPlugin($plugin_slot, $plugin_type); + $class_name = ctools_plugin_get_class($plugin, $class_type); if (!class_exists($class_name)) { throw new Exception("Plugin '$plugin_name' of type '$plugin_type' does not contain a valid class name in handler slot '$class_type'", E_WARNING); @@ -450,85 +471,40 @@ abstract class VersioncontrolRepository implements VersioncontrolEntityInterface } /** - * Contains the urls mainly for displaying. + * Base class for URL handlers. */ -class VersioncontrolRepositoryUrlHandler { +class VersioncontrolRepositoryUrlHandler implements VersioncontrolWebviewerUrlHandlerInterface { /** * Repository where this urls belongs. * - * @var VersioncontrolRepository + * @var VersioncontrolRepository */ public $repository; /** - * An array of repository viewer URLs. + * The first part of the URL that is going to be prepended to each URL + * retrieved. * - * @var array + * @var string */ - public $urls; + public $base_url; - public function __construct($repository, $urls) { + /** + * An array of template URLs for the web viewer. + * + * @var array + */ + public $template_urls; + + public function __construct($repository, $base_url, $template_urls) { $this->repository = $repository; - $this->urls = $urls; + $this->base_url = $base_url; + $this->template_urls = $template_urls; } - /** - * Explain and return and empty array of urls data member. - */ - public static function getEmpty() { - return array( - /** - * The URL of the repository viewer that displays a given commit in the - * repository. "%revision" is used as placeholder for the - * revision/commit/changeset identifier. - */ - 'commit_view' => '', - /** - * The URL of the repository viewer that displays the commit log of a - * given file in the repository. "%path" is used as placeholder for the - * file path, "%revision" will be replaced by the file-level revision - * (the one in {versioncontrol_item_revisions}.revision), and "%branch" - * will be replaced by the branch name that the file is on. - */ - 'file_log_view' => '', - /** - * The URL of the repository viewer that displays the contents of a given - * file in the repository. "%path" is used as placeholder for the file - * path, "%revision" will be replaced by the file-level revision (the one - * in {versioncontrol_item_revisions}.revision), and "%branch" will be - * replaced by the branch name that the file is on. - */ - 'file_view' => '', - /** - * The URL of the repository viewer that displays the contents of a given - * directory in the repository. "%path" is used as placeholder for the - * directory path, "%revision" will be replaced by the file-level revision - * (the one in {versioncontrol_item_revisions}.revision - only makes sense - * if directories are versioned, of course), and "%branch" will be - * replaced by the branch name that the directory is on. - */ - 'directory_view' => '', - /** - * The URL of the repository viewer that displays the diff between two - * given files in the repository. "%path" and "%old-path" are used as - * placeholders for the new and old paths (for some version control - * systems, like CVS, those paths will always be the same). - * "%new-revision" and "%old-revision" will be replaced by the - * respective file-level revisions (from - * {versioncontrol_item_revisions}.revision), and "%branch" will be - * replaced by the branch name that the file is on. - */ - 'diff' => '', - /** - * The URL of the issue tracker that displays the issue/case/bug page of - * an issue id which presumably has been mentioned in a commit message. - * As issue tracker URLs are likely specific to each repository, this is - * also a per-repository setting. (Although... maybe it would make sense - * to have per-project rather than per-repository. Oh well.) - */ - 'tracker' => '' - ); + public function getTemplateUrl($name) { + return sprintf('%s/%s', $this->baseUrl, $this->template_urls[$name]); } /** @@ -547,7 +523,7 @@ class VersioncontrolRepositoryUrlHandler { if (empty($revision)) { return ''; } - return strtr($this->urls['commit_view'], array( + return strtr($this->getTemplateUrl('commit_view'), array( '%revision' => $revision, )); } @@ -571,12 +547,12 @@ class VersioncontrolRepositoryUrlHandler { $label = $item->getSelectedLabel(); if (isset($label->type) && $label->type == VERSIONCONTROL_LABEL_BRANCH) { - $current_branch = $label['name']; + $current_branch = $label->name; } - if (!empty($this->urls['file_log_view'])) { + if (!empty($this->template_urls['file_log_view'])) { if ($item->isFile()) { - return strtr($this->urls['file_log_view'], array( + return strtr($this->getTemplateUrl('file_log_view'), array( '%path' => $item->path, '%revision' => $item->revision, '%branch' => isset($current_branch) ? $current_branch : '', @@ -593,6 +569,7 @@ class VersioncontrolRepositoryUrlHandler { if (isset($current_branch)) { $query['branches'] = $current_branch; } + // FIXME change this to current view return url('commitlog', array( 'query' => $query, 'absolute' => TRUE, @@ -620,12 +597,12 @@ class VersioncontrolRepositoryUrlHandler { $current_branch = $label->name; } $view_url = $item->isFile() - ? $this->urls['file_view'] - : $this->urls['directory_view']; + ? $this->getTemplateUrl('file_view') + : $this->getTemplateUrl('directory_view'); return strtr($view_url, array( - '%path' => $item['path'], - '%revision' => $item['revision'], + '%path' => $item->path, + '%revision' => $item->revision, '%branch' => isset($current_branch) ? $current_branch : '', )); } @@ -647,14 +624,14 @@ class VersioncontrolRepositoryUrlHandler { public function getDiffUrl(&$file_item_new, $file_item_old) { $label = $file_item_new->getSelectedLabel(); - if (isset($label['type']) && $label['type'] == VERSIONCONTROL_LABEL_BRANCH) { - $current_branch = $label['name']; + if (isset($label->type) && $label->type == VERSIONCONTROL_LABEL_BRANCH) { + $current_branch = $label->name; } - return strtr($this->urls['diff'], array( - '%path' => $file_item_new['path'], - '%new-revision' => $file_item_new['revision'], - '%old-path' => $file_item_old['path'], - '%old-revision' => $file_item_old['revision'], + return strtr($this->getTemplateUrl('diff'), array( + '%path' => $file_item_new->path, + '%new-revision' => $file_item_new->revision, + '%old-path' => $file_item_old->path, + '%old-revision' => $file_item_old->revision, '%branch' => isset($current_branch) ? $current_branch : '', )); } @@ -673,6 +650,6 @@ class VersioncontrolRepositoryUrlHandler { * An empty string is returned if no issue tracker URL has been defined. */ public function getTrackerUrl($issue_id) { - return strtr($this->urls['tracker'], array('%d' => $issue_id)); + return strtr($this->getTrackerUrl('tracker'), array('%d' => $issue_id)); } } diff --git includes/interfaces.inc includes/interfaces.inc index 8ef4ffa..c5a559d 100644 --- includes/interfaces.inc +++ includes/interfaces.inc @@ -322,3 +322,82 @@ interface VersioncontrolAuthHandlerInterface { */ public function getErrorMessages(); } + +interface VersioncontrolWebviewerUrlHandlerInterface { + + /** + * Retrieve the URL of the repository viewer that displays the given commit + * in the corresponding repository. + * + * @param $revision + * The revision on the commit operation whose view URL should be retrieved. + * + * @return + * The commit view URL corresponding to the given arguments. + * An empty string is returned if no commit view URL has been defined, + * or if the commit cannot be viewed for any reason. + */ + public function getCommitViewUrl($revision); + + /** + * Retrieve the URL of the repository viewer that displays the commit log + * of the given item in the corresponding repository. If no such URL has been + * specified by the user, the appropriate URL from the Commit Log module is + * used as a fallback (if that module is enabled). + * + * @param $item + * The item whose log view URL should be retrieved. + * + * @return + * The item log view URL corresponding to the given arguments. + * An empty string is returned if no item log view URL has been defined + * (and if not even Commit Log is enabled), or if the item cannot be viewed + * for any reason. + */ + public function getItemLogViewUrl(&$item); + + /** + * Retrieve the URL of the repository viewer that displays the contents of the + * given item in the corresponding repository. + * + * @param $item + * The item whose view URL should be retrieved. + * + * @return + * The item view URL corresponding to the given arguments. + * An empty string is returned if no item view URL has been defined, + * or if the item cannot be viewed for any reason. + */ + public function getItemViewUrl(&$item); + + /** + * Retrieve the URL of the repository viewer that displays the diff between + * two given files in the corresponding repository. + * + * @param $file_item_new + * The new version of the file that should be diffed. + * @param $file_item_old + * The old version of the file that should be diffed. + * + * @return + * The diff URL corresponding to the given arguments. + * An empty string is returned if no diff URL has been defined, + * or if the two items cannot be diffed for any reason. + */ + public function getDiffUrl(&$file_item_new, $file_item_old); + + /** + * Retrieve the URL of the issue tracker that displays the issue/case/bug page + * of an issue id which presumably has been mentioned in a commit message. + * As issue tracker URLs are specific to each repository, this also needs + * to be given as argument. + * + * @param $issue_id + * A number that uniquely identifies the mentioned issue/case/bug. + * + * @return + * The issue tracker URL corresponding to the given arguments. + * An empty string is returned if no issue tracker URL has been defined. + */ + public function getTrackerUrl($issue_id); +} diff --git includes/plugins/webviewer_url_handlers/gitweb.inc includes/plugins/webviewer_url_handlers/gitweb.inc new file mode 100644 index 0000000..b269809 --- /dev/null +++ includes/plugins/webviewer_url_handlers/gitweb.inc @@ -0,0 +1,93 @@ + t('Gitweb URL autogenerator'), + 'url_templates' => array( + + /** + * Template url for the commit view. + * + * The URL of the repository viewer that displays a given commit in the + * repository. + * It contains the following placeholders: + * - "%revision" for the revision/commit/changeset identifier. + */ + 'commit_view' => 'hola', + + /** + * Template url for the file log view. + * + * The URL of the repository viewer that displays the commit log of + * a given file in the repository. + * It contains the following placeholders: + * - "%path" for the file path + * - "%revision" will be replaced by the file-level revision (the one + * in {versioncontrol_item_revisions}.revision) + * - "%branch" will be replaced by the branch name that the file is on + */ + 'file_log_view' => '', + + /** + * Template url for the file view. + * + * The URL of the repository viewer that displays the contents of a + * given file in the repository. + * It contains the following placeholders: + * - "%path" for the file path + * - "%revision" will be replaced by the file-level revision (the + * one in {versioncontrol_item_revisions}.revision) + * - "%branch" will be replaced by the branch name that the file is on + */ + 'file_view' => '', + + /** + * Template url for the directory view. + * + * The URL of the repository viewer that displays the contents of a given + * directory in the repository. + * It contains the following placeholders: + * - "%path" for the directory path + * - "%revision" will be replaced by the file-level revision (the + * one in {versioncontrol_item_revisions}.revision - only makes sense + * if directories are versioned, of course) + * - "%branch" will be replaced by the branch name that the + * directory is on. + */ + 'directory_view' => '', + + /** + * Template url for the diff view. + * + * The URL of the repository viewer that displays the diff between two + * given files in the repository. + * It contains the following placeholders: + * - "%path" and "%old-path" for the new and old paths (for some + * version control systems, like CVS, those paths will always be + * the same). + * - "%new-revision" and "%old-revision" will be replaced by the + * respective file-level revisions (from + * {versioncontrol_item_revisions}.revision) + * - "%branch" will be replaced by the branch name that the file is on. + */ + 'diff' => '', + + /** + * Template url for the issue tracker view. + * + * The URL of the issue tracker that displays the issue/case/bug page of + * an issue id which presumably has been mentioned in a commit message. + * As issue tracker URLs are likely specific to each repository, this is + * also a per-repository setting. (Although... maybe it would make sense + * to have per-project rather than per-repository. Oh well.) + */ + 'tracker' => '' + + ), + 'handler' => array( + 'class' => 'VersioncontrolRepositoryUrlHandler', + ), +); diff --git includes/views/handlers/versioncontrol_handler_field_operation_revision.inc includes/views/handlers/versioncontrol_handler_field_operation_revision.inc index 45ba0e5..c8fde26 100644 --- includes/views/handlers/versioncontrol_handler_field_operation_revision.inc +++ includes/views/handlers/versioncontrol_handler_field_operation_revision.inc @@ -7,16 +7,32 @@ */ class versioncontrol_handler_field_operation_revision extends views_handler_field { public $backends = NULL; + public $repos = array(); function construct() { parent::construct(); $this->backends = versioncontrol_get_backends(); } + /** + * Get a repository of the @param $vcs type. + */ + function getRepository($vcs, $repo_id) { + if (!isset($this->repos[$repo_id])) { + $this->repos[$repo_id] = $this->backends[$vcs]->loadEntity('repo', array($repo_id)); + } + return $this->repos[$repo_id]; + } + function render($values) { $revision = $values->{$this->field_alias}; $vcs = $values->{$this->aliases['vcs']}; + $repo_id = $values->{$this->aliases['repo_id']}; + var_dump($values); $revision = $this->backends[$vcs]->formatRevisionIdentifier($revision, 'short'); + $repo = $this->getRepository($vcs, $repo_id); + var_dump($repo); + dpm($repo->getUrlHandler()); return check_plain($revision); } } diff --git includes/views/versioncontrol.views.inc includes/views/versioncontrol.views.inc index 4de3c8f..c7a7c58 100755 --- includes/views/versioncontrol.views.inc +++ includes/views/versioncontrol.views.inc @@ -291,6 +291,7 @@ function versioncontrol_views_data() { 'click sortable' => TRUE, 'additional fields' => array( 'vcs' => array('table' => 'versioncontrol_repositories', 'field' => 'vcs'), + 'repo_id' => array('table' => 'versioncontrol_repositories', 'field' => 'repo_id'), ), ), 'sort' => array( @@ -461,6 +462,7 @@ function versioncontrol_views_data() { 'click sortable' => TRUE, 'additional fields' => array( 'vcs' => array('table' => 'versioncontrol_repositories', 'field' => 'vcs'), + 'repo_id' => array('table' => 'versioncontrol_repositories', 'field' => 'repo_id'), ), ), 'sort' => array( diff --git tests/VersioncontrolTestCase.test tests/VersioncontrolTestCase.test index 4799be6..430d5d8 100644 --- tests/VersioncontrolTestCase.test +++ tests/VersioncontrolTestCase.test @@ -149,9 +149,14 @@ abstract class VersioncontrolTestCase extends DrupalWebTestCase { 'auth_handler' => 'ffa', 'author_mapper' => 'simple_mail', 'committer_mapper' => 'simple_mail', + //FIXME use a general commitlog url handler + 'webviewer_url_handler' => 'gitweb', ); $data += $default_data; + if (!isset($data['data']['versioncontrol']['base_url'])) { + $data['data']['versioncontrol']['base_url'] = ''; + } foreach ($default_plugins as $plugin_slot => $default_plugin) { if (empty($data['plugins'][$plugin_slot])) { $data['plugins'][$plugin_slot] = $default_plugin; diff --git versioncontrol.admin.inc versioncontrol.admin.inc index ac81a9d..debd50e 100644 --- versioncontrol.admin.inc +++ versioncontrol.admin.inc @@ -371,6 +371,31 @@ function versioncontrol_admin_repository_edit(&$form_state, $repository, $vcs = '#options' => versioncontrol_auth_handlers_get_names(), ); + $repo_url_handler = $repository_exists ? $repository->getUrlHandler() : NULL; + + $form['repository_urls'] = array( + '#type' => 'fieldset', + '#title' => t('Repository browser URL handler'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + '#weight' => 5, + ); + $form['repository_urls']['base_url'] = array( + '#type' => 'textfield', + '#title' => t('Base URL'), + '#default_value' => isset($repo_url_handler) ? $repo_url_handler->base_url : '', + '#size' => 40, + '#maxlength' => 255, + '#description' => t('The URL that let you see the repository web viewer. Do not use a trailing slash.'), + ); + $form['repository_urls']['url_handler'] = array( + '#type' => 'radios', + '#title' => t('Web viewer URL handler'), + '#description' => t('The handler which will provide the URLs that will be used to add links to item and commit displays such as the commit log.'), + '#default_value' => $repository_exists ? $repository->plugins['webviewer_url_handler'] : 0, + '#options' => versioncontrol_webviewer_url_handlers_get_names(), + ); + $form['submit'] = array( '#type' => 'submit', '#value' => t('Save repository'), @@ -414,6 +439,7 @@ function versioncontrol_admin_repository_edit_submit($form, &$form_state) { 'author_mapper' => $form_state['values']['author_mapper'], 'committer_mapper' => $form_state['values']['committer_mapper'], 'auth_handler' => $form_state['values']['auth_handler'], + 'webviewer_url_handler' => $form_state['values']['url_handler'], ); } else { @@ -428,11 +454,14 @@ function versioncontrol_admin_repository_edit_submit($form, &$form_state) { 'author_mapper' => $form_state['values']['author_mapper'], 'committer_mapper' => $form_state['values']['committer_mapper'], 'auth_handler' => $form_state['values']['auth_handler'], + 'webviewer_url_handler' => $form_state['values']['url_handler'], ), ); $repository = $backends[$form['#vcs']]->buildEntity('repo', $repository); } + $repository->data['versioncontrol']['base_url'] = $form_state['values']['base_url']; + // Let other modules alter the repository array. foreach (module_implements('versioncontrol_repository_submit') as $module) { $function = $module .'_versioncontrol_repository_submit'; diff --git versioncontrol.module versioncontrol.module index 3bcff80..3cdfa5d 100644 --- versioncontrol.module +++ versioncontrol.module @@ -1009,3 +1009,18 @@ function versioncontrol_auth_handlers_get_names() { asort($names); return $names; } + +/** + * Load the names of all 'webviewer_url_handlers' for use at forms. + */ +function versioncontrol_webviewer_url_handlers_get_names() { + ctools_include('plugins'); + + $names = array(); + foreach (ctools_get_plugins('versioncontrol', 'webviewer_url_handlers') as $name => $plugin) { + $names[$name] = $plugin['title']; + } + + asort($names); + return $names; +}