Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit, really

  • Loading branch information...
commit e1ff1b6e316b670dc93e279b15a2e16591bb558e 0 parents
@conover conover authored
1  .gitignore
@@ -0,0 +1 @@
+.DS_Store
1  README.markdown
@@ -0,0 +1 @@
+Install by extracting into your activeCollab install directory then activating via the modules admin page.
118 activecollab/application/modules/github/GithubModule.class.php
@@ -0,0 +1,118 @@
+<?php
+
+ /**
+ * Github module definition
+ *
+ * @package activeCollab.modules.github
+ * @subpackage models
+ */
+ class GithubModule extends Module {
+
+ /**
+ * Plain module name
+ *
+ * @var string
+ */
+ var $name = 'github';
+
+ /**
+ * Is system module flag
+ *
+ * @var boolean
+ */
+ var $is_system = false;
+
+ /**
+ * Module version
+ *
+ * @var string
+ */
+ var $version = '1.0';
+
+ // ---------------------------------------------------
+ // Events and Routes
+ // ---------------------------------------------------
+
+ /**
+ * Define module routes
+ *
+ * @param Router $r
+ * @return null
+ */
+ function defineRoutes(&$router) {
+ $router->map( 'github',
+ 'projects/:project_id/github',
+ array('controller' => 'github', 'action' => 'index'),
+ array('project_id' => '\d+'));
+ $router->map( 'github_add',
+ 'project/:project_id/github_add',
+ array('controller' => 'github', 'action' => 'add'),
+ array('project_id' => '\d+'));
+ $router->map( 'github_repository_history',
+ '/projects/:project_id/github_repositories/:github_repository_id/history',
+ array('controller' => 'github', 'action'=>'history'),
+ array('project_id' => '\d+', 'github_repository_id' => '\d+'));
+ $router->map( 'github_commit_filepaths',
+ 'projects/:project_id/github_repositories/:github_repository_id/commit/',
+ array('controller' => 'github', 'action' => 'commit_filepaths'),
+ array('project_id' => '\d+', 'github_repository_id' => '\d+'));
+ } // defineRoutes
+
+ /**
+ * Define event handlers
+ *
+ * @param EventsManager $events
+ * @return null
+ */
+ function defineHandlers(&$events) {
+ //$events->listen('on_project_options', 'on_project_options');
+ $events->listen('on_project_tabs', 'on_project_tabs');
+ } // defineHandlers
+
+ /**
+ * Install this module
+ *
+ * @param void
+ * @return boolean
+ */
+ function install() {
+ mkdir(WORK_PATH.'/export', 0777);
+ return parent::install();
+ } // install
+
+ // ---------------------------------------------------
+ // Names
+ // ---------------------------------------------------
+
+ /**
+ * Get module display name
+ *
+ * @return string
+ */
+ function getDisplayName() {
+ return lang('Github');
+ } // getDisplayName
+
+ /**
+ * Return module description
+ *
+ * @param void
+ * @return string
+ */
+ function getDescription() {
+ return lang('Github repository integration');
+ } // getDescription
+
+ /**
+ * Return module uninstallation message
+ *
+ * @param void
+ * @return string
+ */
+ function getUninstallMessage() {
+ return lang('Module will be deactivated. Files created with this module will not be deleted');
+ } // getUninstallMessage
+
+ }
+
+?>
264 activecollab/application/modules/github/controllers/GithubController.class.php
@@ -0,0 +1,264 @@
+<?php
+
+ // We need project controller
+ use_controller('project', SYSTEM_MODULE);
+
+ /**
+ * Github controller
+ *
+ * @package activeCollab.modules.github
+ * @subpackage controllers
+ */
+ class GithubController extends ProjectController {
+
+ /**
+ * Controller name
+ *
+ * @var string
+ */
+ var $controller_name = 'github';
+
+ /**
+ * Active module
+ *
+ * @var string
+ */
+ var $active_module = GITHUB_MODULE;
+
+ /**
+ * Active repository
+ *
+ * @var Repository
+ */
+ var $active_repository = null;
+
+ /**
+ * Constructor
+ *
+ * @param Request $request
+ * @return TicketsController
+ */
+ function __construct($request) {
+ parent::__construct($request);
+
+ if(!$this->logged_user->isAdministrator() && !$this->logged_user->isProjectLeader($this->active_project) && !$this->logged_user->isProjectManager()) {
+ $this->httpError(HTTP_ERR_FORBIDDEN);
+ } // if
+
+ $repository_id = $this->request->get('github_repository_id');
+ $this->active_repository = GithubRepositories::findById($repository_id);
+ if (!instance_of($this->active_repository, 'GithubRepository')) {
+ $this->active_repository = new GithubRepository();
+ }
+
+ $this->wireframe->print_button = false;
+ $this->smarty->assign(array(
+ 'active_repository' => $this->active_repository,
+ 'project_tab' => GITHUB_MODULE,
+ 'page_tab' => 'github',
+ 'add_repository_url' => github_module_add_repository_url($this->active_project)
+ ));
+ }
+
+ /**
+ * Index - main page for github
+ *
+ * @param void
+ * @return null
+ */
+ function index() {
+ if(Repository::canAdd($this->logged_user, $this->active_project)) {
+ $this->wireframe->addPageAction(lang('Add Github Repository'), github_module_add_repository_url($this->active_project));
+ } // if
+
+ $repositories = GithubRepositories::findByProjectId($this->active_project->getId());
+
+ $this->smarty->assign(array(
+ 'repositories' => $repositories,
+ ));
+
+ } // index
+
+ /**
+ * Add a Github repository
+ *
+ * @return void
+ * @return null
+ **/
+ function add()
+ {
+ if(!Repository::canAdd($this->logged_user, $this->active_project)) {
+ $this->httpError(HTTP_ERR_FORBIDDEN);
+ } // if
+
+ $repository_data = $this->request->post('repository');
+ if(!is_array($repository_data)) {
+ $repository_data = array(
+ 'visibility' => $this->active_project->getDefaultVisibility(),
+ );
+ } // if
+
+ if ($this->request->isSubmitted()) {
+ $repository_data['name'] = trim($repository_data['name']) == '' ? $repository_data['url'] : $repository_data['name'];
+ $this->active_repository->setAttributes($repository_data);
+ $this->active_repository->setBody(clean(array_var($repository_data, 'url', null)));
+ $this->active_repository->setProjectId($this->active_project->getId());
+ $this->active_repository->setCreatedBy($this->logged_user);
+ $this->active_repository->setState(STATE_VISIBLE);
+
+ $result = $this->active_repository->testRepositoryConnection();
+ if ($result === true) {
+ $save = $this->active_repository->save();
+ if ($save && !is_error($save)) {
+ flash_success(lang('Project repository &quot;:name&quot; has been added successfully'), array('name'=>$this->active_repository->getName()));
+ $this->redirectToUrl(github_module_url($this->active_project));
+ } else {
+ $save->errors['-- any --'] = $save->errors['body'];
+ $this->smarty->assign('errors', $save);
+ } //if
+ }
+ else {
+ $errors = new ValidationErrors();
+ $errors->addError(lang('Failed to connect to repository: :message', array('message'=>$result)));
+ $this->smarty->assign('errors', $errors);
+ } // if
+ } // if
+
+ $this->smarty->assign(Array(
+ 'repository_data' => $repository_data
+ ));
+ } // add
+
+ /**
+ * Repository history
+ *
+ * @return void
+ * @author Chris Conover
+ **/
+ function history()
+ {
+ $page = intval(array_var($_GET, 'page')) > 0 ? array_var($_GET, 'page') : 1;
+ $branch_tag = strval(array_var($_GET, 'branch_tag')) != '' ? array_var($_GET, 'branch_tag'): 'master';
+
+ js_assign( 'commit_filepath_url',
+ assemble_url('github_commit_filepaths',
+ array('project_id' => $this->active_project->getId(),
+ 'github_repository_id' => $this->active_repository->getID()
+ )
+ )
+ );
+
+ $commits = $this->active_repository->getBranchTagCommits($branch_tag, $page);
+
+ // Group commits by days
+ $grouped_commits = Array();
+ $date_format = 'F j. Y';
+ foreach($commits as $commit) {
+ $commit->short_id = substr($commit->id, 0, 9).'...'.substr($commit->id, -9);
+ $commit->message = $this->analyzeCommitMessage('Ticket #1: '.$commit->message);
+
+ $commit_formatted_date = date($date_format, strtotime($commit->committed_date));
+ if(count($grouped_commits) == 0 || !isset($grouped_commits[$commit_formatted_date])) {
+ $grouped_commits[$commit_formatted_date] = Array($commit);
+ } else {
+ array_push($grouped_commits[$commit_formatted_date], $commit);
+ }
+ }
+
+ $this->smarty->assign(Array(
+ 'path_info' => strval(array_var($_GET, 'path_info')),
+ 'page' => $page,
+ 'next_page' => ($page + 1),
+ 'prev_page' => (($page > 1) ? ($page - 1) : null),
+ 'branches' => $this->active_repository->getBranches(),
+ 'tags' => $this->active_repository->getTags(),
+ 'commits' => $grouped_commits,
+ 'user' => $this->active_repository->getUserName(),
+ 'repo' => $this->active_repository->getRepoName(),
+ 'branch_tag' => $branch_tag
+ ));
+ }
+
+ /**
+ * Add AC object links to commit messages
+ *
+ * @param string
+ * @return string
+ **/
+ private function analyzeCommitMessage($commit_message)
+ {
+ $pattern = '/(ticket|milestone|discussion|task)[s]*[\s]+[#]*(\d+)/i';
+
+ if (preg_match_all($pattern, $commit_message, $matches)) {
+ $i = 0;
+ $search = array();
+ $replace = array();
+
+ $matches_unique = array_unique($matches['0']);
+
+ foreach ($matches_unique as $key => $match) {
+ $match_data = preg_split('/[\s,]+/', $match, null, PREG_SPLIT_NO_EMPTY);
+
+ $object_class_name = $match_data['0'];
+ $module_name = Inflector::pluralize($object_class_name);
+ $object_id = trim($match_data['1'], '#');
+ $search[$i] = $match;
+
+ if (class_exists($module_name) && class_exists($object_class_name)) {
+ $object = null;
+
+ switch (strtolower($module_name)) {
+ case 'tickets':
+ $object = Tickets::findByTicketId($this->active_project, $object_id);
+ break;
+ case 'discussions':
+ $object = Discussions::findById($object_id);
+ break;
+ case 'milestones':
+ $object = Milestones::findById($object_id);
+ break;
+ case 'tasks' :
+ $object = Tasks::findById($object_id);
+ break;
+ } // switch
+
+ if (instance_of($object, $object_class_name)) {
+ $replace[$i] = '<a href="'.$object->getViewUrl().'">'.$match_data['0'].' '.$match_data['1'].'</a>';
+ }
+ else {
+ $replace[$i] = '<a href="#" class="project_object_missing" title="'.lang('Project object does not exist in this project').'">'.$match_data['0'].' '.$match_data['1'].'</a>';
+ } // if instance_of
+
+ $i++;
+ } // if module loaded
+ } // foreach
+
+ return str_ireplace($search, $replace, htmlspecialchars($commit_message)); // linkify
+ } // if preg_match
+ return $commit_message;
+ }
+
+ /**
+ * Fetch git commit file paths
+ *
+ *
+ * @return JSON
+ **/
+ function commit_filepaths()
+ {
+ $errors = Array();
+ $commit_id = strval(array_var($_GET, 'commit_id')) != '' ? array_var($_GET, 'commit_id'): '';
+
+ if($commit_id == '') {
+ array_push($errors, 'Invalid commit id');
+ } else {
+ if( is_array($response = $this->active_repository->getCommitFilePaths($commit_id)) ) {
+ $this->serveData($response, 'file_paths', null, FORMAT_JSON);
+ } else {
+ array_push($errors, $response);
+ }
+ }
+ $this->serveData(Array('errors' => $errors), null, FORMAT_JSON);
+ }
+ }
+?>
25 activecollab/application/modules/github/handlers/on_project_tabs.php
@@ -0,0 +1,25 @@
+<?php
+
+ /**
+ * Github control module on_project_tabs event handler
+ *
+ * @package activeCollab.modules.github
+ * @subpackage handlers
+ */
+
+ /**
+ * Handle on prepare project overview event
+ *
+ * @param NamedList $tabs
+ * @param User $logged_user
+ * @param Project $project
+ * @return null
+ */
+ function github_handle_on_project_tabs(&$tabs, &$logged_user, &$project) {
+ $tabs->add('github', array(
+ 'text' => lang('Github'),
+ 'url' => github_module_url($project)
+ ));
+ } // github_handle_on_project_tabs
+
+?>
37 activecollab/application/modules/github/init.php
@@ -0,0 +1,37 @@
+<?php
+
+ /**
+ * Github module initialization file
+ *
+ * @package activeCollab.modules.project_exporter
+ */
+
+ define('GITHUB_MODULE', 'github');
+ define('GITHUB_MODULE_PATH', APPLICATION_PATH . '/modules/github');
+
+ require_once GITHUB_MODULE_PATH.'/models/GithubRepository.class.php';
+ require_once GITHUB_MODULE_PATH.'/models/GithubRepositories.class.php';
+
+ /**
+ * Return section URL
+ *
+ * @param Project $project
+ * @param array $additional_params
+ * @return string
+ */
+ function github_module_url($project, $additional_params = null) {
+ $params = array('project_id' => $project->getId());
+ return assemble_url('github', $params);
+ }
+
+ /**
+ * Get the URL to add a repository
+ *
+ * @param object $project
+ * @return string
+ */
+ function github_module_add_repository_url($project) {
+ return assemble_url('github_add',array('project_id'=>$project->getId()));
+ } // add a repository URL
+
+?>
74 activecollab/application/modules/github/models/GithubRepositories.class.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Repositories
+ *
+ */
+class GithubRepositories extends ProjectObjects {
+
+ /**
+ * Get repositories by project id and add last commit info
+ *
+ * @param int $project_id
+ * @return array of objects
+ */
+ function findByProjectId($project_id) {
+ $repositories = ProjectObjects::find(array(
+ 'conditions' => "project_id = $project_id AND `type` = 'GithubRepository' AND state >='".STATE_VISIBLE."'",
+ 'order' => 'id asc'
+ ));
+
+ if (is_foreachable($repositories)) {
+ foreach ($repositories as $repository) {
+ $repository->last_commit = $repository->getLastCommit();
+ if(!is_null($repository->last_commit) && !is_int($repository->last_commit->committed_date)) {
+ $repository->last_commit->committed_date = strtotime($repository->last_commit->committed_date);
+ }
+
+ $repository->last_tag = $repository->getLastTag();
+ }
+ }
+
+ return $repositories;
+ } // find repositories by project id
+
+
+ /**
+ * Find all repositories that match specific update type
+ *
+ * @param int $update_type
+ * @return array
+ */
+ function findByUpdateType($update_type) {
+ return ProjectObjects::find(array(
+ 'conditions' => "`type` = 'GithubRepository' AND integer_field_2 = '$update_type' AND state != '".STATE_DELETED."'"
+ ));
+ } // find repositories by update type
+
+ // ---------------------------------------------------
+ // Portal methods
+ // ---------------------------------------------------
+
+ /**
+ * Return repository which was first added and last commit info
+ *
+ * @param Project $project
+ * @return array
+ */
+ function findByPortalProject($project) {
+ $repository = ProjectObjects::find(array(
+ 'conditions' => array('project_id = ? AND type = ? AND state >= ?', $project->getId(), 'GithubRepository', STATE_VISIBLE),
+ 'order' => 'created_on ASC',
+ 'one' => true
+ ));
+
+ if(instance_of($repository, 'GithubRepository')) {
+ $repository->last_commit = $repository->getLastCommit();
+ } // if
+
+ return $repository;
+ } // findByPortalProject
+
+}
+
+?>
417 activecollab/application/modules/github/models/GithubRepository.class.php
@@ -0,0 +1,417 @@
+<?php
+
+ /**
+ * Repository class
+ *
+ * @package activeCollab.modules.source
+ */
+ class GithubRepository extends ProjectObject {
+
+ private static $API_URLS = Array(
+ 'REPO_INFO' => 'https://github.com/api/v2/json/repos/show/%s/%s',
+ 'BRANCHES' => 'https://github.com/api/v2/json/repos/show/%s/%s/branches',
+ 'TAGS' => 'https://github.com/api/v2/json/repos/show/%s/%s/tags',
+ 'COMMITS' => 'http://github.com/api/v2/json/commits/list/%s/%s/%s',
+ 'COMMIT' => 'http://github.com/api/v2/json/commits/show/%s/%s/%s');
+
+ /**
+ * Permission name
+ *
+ * @var string
+ */
+ var $permission_name = 'github_repository';
+
+ /**
+ * Project tab (compatibility with rel 1.1)
+ *
+ * @var string
+ */
+ var $project_tab = 'github';
+
+ /**
+ * Log object activities
+ *
+ * @var booelan
+ */
+ var $log_activities = false;
+
+ /**
+ * Repositories can have subscribers
+ *
+ * @var boolean
+ */
+ var $can_have_subscribers = true;
+
+ /**
+ * Name of the route used for portal view URL
+ *
+ * @var string
+ */
+ var $portal_view_route_name = 'portal_github_repository';
+
+ /**
+ * Name of the object ID variable used alongside project_id when URL-s are
+ * generated (eg. comment_id, task_id, message_category_id etc)
+ *
+ * @var string
+ */
+ var $object_id_param_name = 'github_repository_id';
+
+ /**
+ * Fields used by this module
+ *
+ * @var array
+ */
+ var $fields = array(
+ 'id',
+ 'type', 'module',
+ 'project_id',
+ 'name',
+ 'body',
+ 'created_on', 'created_by_id', 'created_by_name', 'created_by_email',
+ 'updated_on', 'updated_by_id', 'updated_by_name', 'updated_by_email',
+ 'varchar_field_1', // username
+ 'varchar_field_2', // password
+ 'text_field_1', // repository url
+ 'state', 'visibility',
+ );
+
+ /**
+ * Field map
+ *
+ * @var array
+ */
+ var $field_map = array(
+ 'root_path' => 'body',
+ 'username' => 'varchar_field_1',
+ 'password' => 'varchar_field_2',
+ 'url' => 'text_field_1'
+ );
+
+
+ /**
+ * Construct a new repository
+ *
+ * @param int $id
+ */
+ function __construct() {
+ parent::__construct();
+
+ $this->setModule(GITHUB_MODULE);
+
+ } // __construct
+
+ /**
+ * Returns true if $user can create a new repository in $project
+ *
+ * @param User $user
+ * @param Project $project
+ * @return boolean
+ */
+ function canAdd($user, $project) {
+ return ProjectObject::canAdd($user, $project, 'github_repository');
+ } // canAdd
+
+
+ /**
+ * undocumented function
+ *
+ * @param void
+ * @return True or String
+ **/
+ function testRepositoryConnection()
+ {
+ if( ($url_info = $this->userRepoFromUrl()) !== False) {
+ $request_url = sprintf(GithubRepository::$API_URLS['REPO_INFO'], $url_info['user'], $url_info['repo']);
+ if(is_object($response = GithubRepository::githubAPIRequest($request_url))) {
+ return True;
+ } else {
+ return $response;
+ }
+ } else {
+ return 'Unable to extract username and repository name from URL string.';
+ }
+ }
+
+ /**
+ * Fetch branch list
+ *
+ * @param void
+ * @return array or null
+ **/
+ function getBranches()
+ {
+ $cache_key = 'github_branches-'.$this->getRepoName();
+ $cache_duration = 60 * 10; // 10 minutes
+
+ if( !is_null($cache_val = cache_get($cache_key)) && $cache_val[1] > (time() - $cache_duration) ) {
+ return $cache_val[0];
+ } else {
+ if( ($url_info = $this->userRepoFromUrl()) !== False) {
+ $request_url = sprintf(GithubRepository::$API_URLS['BRANCHES'], $url_info['user'], $url_info['repo']);
+ if( is_object($response = GithubRepository::githubAPIRequest($request_url)) ) {
+ cache_set($cache_key, Array($response->branches, time()));
+ return $response->branches;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Fetch tag list
+ *
+ * @param void
+ * @return array
+ **/
+ function getTags()
+ {
+ $cache_key = 'github_tags-'.$this->getRepoName();
+ $cache_duration = 60 * 10; // 10 minutes
+
+ if( !is_null($cache_val = cache_get($cache_key)) && $cache_val[1] > (time() - $cache_duration) ) {
+ return $cache_val[0];
+ } else {
+ if( ($url_info = $this->userRepoFromUrl()) !== False) {
+ $request_url = sprintf(GithubRepository::$API_URLS['TAGS'], $url_info['user'], $url_info['repo']);
+ if( is_object($response = GithubRepository::githubAPIRequest($request_url)) ) {
+ cache_set($cache_key, Array($response->tags, time()));
+ return $response->tags;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Get last tag
+ *
+ * @return void
+ * @author Chris Conover
+ **/
+ function getLastTag()
+ {
+ if($this->testRepositoryConnection() !== False) {
+ if( !is_null($tags = $this->getTags()) ) {
+ /*
+ // Checks the most recent commit of each tag and select the one
+ // with the most recent commit
+ $last_tag = null;
+ $last_commit = null;
+ foreach($response->tags as $name => $hash) {
+ if( is_array($_response = $this->getBranchTagCommits($name))) {
+ if( is_null($last_commit) || strtotime($_response[0]->committed_date) > strtotime($last_commit->committed_date)) {
+ $last_commit = $_response[0];
+ $last_tag = Array('name' => $name, 'hash' => $hash, 'commit' => $last_commit);
+ }
+ }
+ }
+ return $last_tag;
+ */
+
+ $tag_names = Array(); // Some kind of iterable object is returned, array_keys doesn't work
+ foreach($tags as $name => $hash) array_push($tag_names, $name);
+ usort($tag_names, create_function('$a,$b', 'return version_compare($a, $b);'));
+ return $tag_names[count($tag_names) - 1];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Got through each branch and find the last commit
+ *
+ * @param void
+ * @return string
+ **/
+ function getLastCommit()
+ {
+ $cache_key = 'github_last_commit-'.$this->getRepoName();
+ $cache_duration = 60 * 10; // 10 minutes
+
+ if( !is_null($cache_val = cache_get($cache_key)) && $cache_val[1] > (time() - $cache_duration) ) {
+ return $cache_val[0];
+ } else {
+ $last_commit = null;
+ if(($url_info = $this->userRepoFromUrl()) !== False) {
+ if( !is_null($response = $this->getBranches())) {
+ foreach($response as $name=>$hash) {
+ if( is_array($_response = $this->getBranchTagCommits($name)) ) {
+ if(is_null($last_commit) || strtotime($_response[0]->committed_date) > strtotime($last_commit->committed_date)) {
+ $last_commit = $_response[0];
+ }
+ }
+ }
+ }
+ }
+ if(!is_null($last_commit)) cache_set($cache_key, Array($last_commit, time()));
+ return $last_commit;
+ }
+ }
+
+ /**
+ * Fetch commits associated with a branch or a tag
+ *
+ * @param string
+ * @return array
+ **/
+ function getBranchTagCommits($name, $page = 1)
+ {
+ $cache_key = 'github_branchtag_commits-'.implode('-', Array($this->getRepoName(), $name, $page));
+ $cache_duration = 60 * 10; // 10 minutes
+
+ if( !is_null($cache_val = cache_get($cache_key)) && $cache_val[1] > (time() - $cache_duration) ) {
+ return $cache_val[0];
+ } else {
+
+ if(($url_info = $this->userRepoFromUrl()) !== False) {
+ $request_url = sprintf(GithubRepository::$API_URLS['COMMITS'], $url_info['user'], $url_info['repo'], $name);
+
+ if($page > 0) $request_url .= '?page='.$page;
+
+ if( is_object($response = GithubRepository::githubAPIRequest($request_url)) ) {
+ cache_set($cache_key, Array($response->commits, time()));
+ return $response->commits;
+ } else {
+ $error = $response;
+ }
+ }
+ return $error;
+ }
+ }
+
+ /**
+ * Fetch details of a specific commit
+ *
+ * @return void
+ * @author Chris Conover
+ **/
+ function getCommitFilePaths($hash)
+ {
+ $cache_key = 'github_commit_filepaths-'.$hash;
+ $cache_duration = 60 * 10; // 10 minutes
+
+ if( !is_null($cache_val = cache_get($cache_key)) && $cache_val[1] > (time() - $cache_duration) ) {
+ return $cache_val[0];
+ } else {
+
+ if( ($url_info = $this->userRepoFromUrl()) !== False) {
+ $request_url = sprintf(GithubRepository::$API_URLS['COMMIT'], $url_info['user'], $url_info['repo'], $hash);
+
+ if( is_object($response = GithubRepository::githubAPIRequest($request_url)) ) {
+ $file_paths = Array('added' => Array(), 'modified' => Array(), 'removed' => Array());
+ foreach($response->commit->modified as $mod) {
+ array_push($file_paths['modified'], $mod->filename);
+ }
+ foreach($response->commit->added as $mod) {
+ array_push($file_paths['added'], $mod);
+ }
+ foreach($response->commit->removed as $mod) {
+ array_push($file_paths['removed'], $mod);
+ }
+ cache_set($cache_key, Array($file_paths, time()));
+ return $file_paths;
+ } else {
+ $error = $response;
+ }
+ }
+ return $error;
+ }
+ }
+
+ /**
+ * Get view URL
+ *
+ * @return string
+ */
+ function getViewUrl() {
+ return $this->getHistoryUrl();
+ } // getViewUrl
+
+ /**
+ * Get repository history URL
+ *
+ * @param null
+ * @return string
+ */
+ function getHistoryUrl($commit_author = null) {
+ $params = array('github_repository_id'=>$this->getId(),'project_id'=>$this->getProjectId());
+
+ return assemble_url('github_repository_history', $params);
+ } // get history URL
+
+ /**
+ * Parse user and repo name
+ *
+ * @param void
+ * @return Array
+ **/
+ function userRepoFromUrl()
+ {
+ if(preg_match('/^https:\/\/github.com\/(?<user>[^\/]+)\/(?<repo>[^\/]+)$/', $this->getUrl(), $matches) == 1) {
+ return $matches;
+ } else {
+ return False;
+ }
+ }
+
+ /**
+ * User or org of Github repo
+ *
+ * @param void
+ * @return string or null
+ **/
+ function getUserName()
+ {
+ $url_info = $this->userRepoFromUrl();
+ if($url_info !== False) {
+ return $url_info['user'];
+ }
+ return null;
+ }
+
+ /**
+ * Name of Github Repo
+ *
+ * @param void
+ * @return string or null
+ **/
+ function getRepoName()
+ {
+ $url_info = $this->userRepoFromUrl();
+ if($url_info !== False) {
+ return $url_info['repo'];
+ }
+ return null;
+ }
+
+ /**
+ * Get repository URL
+ *
+ * @return string
+ */
+ function getUrl() {
+ return str_replace(' ', '%20', $this->getFieldValue('text_field_1'));
+ } // getUrl
+
+ /**
+ * Github API Request
+ *
+ * @return void
+ * @author Chris Conover
+ **/
+ private static function githubAPIRequest($url, $data = NULL)
+ {
+ if( ($json = file_get_contents($url)) !== False) {
+ if( !is_null($obj = json_decode($json)) ) {
+ return (isset($obj->errors)) ? $obj->errors : $obj;
+ } else {
+ return 'JSON cannot be decoded or recursion too deep.';
+ }
+ } else {
+ return 'Unable to complete API request.';
+ }
+ }
+ }
+
+?>
32 activecollab/application/modules/github/views/github/_repository_form.tpl
@@ -0,0 +1,32 @@
+{wrap field=name}
+ {label for=repositoryName required=yes}{lang}Name{/lang}{/label}
+ {text_field name='repository[name]' value=$repository_data.name id=repositoryName class='title required'}
+{/wrap}
+
+{wrap field=url aid="$aid_url"}
+ {label for=repositoryUrl required=yes}{lang}Repository URL or directory:{/lang}{/label}
+ {text_field name='repository[url]' value=$repository_data.url id=repositoryUrl class='title required'}
+{/wrap}
+
+<div class="col">
+{wrap field=username}
+ {label for=repositoryUsername}{lang}Username{/lang}{/label}
+ {text_field name='repository[username]' style='width:250px' value=$repository_data.username id=repositoryUsername class='title'}
+{/wrap}
+</div>
+
+<div class="col">
+{wrap field=password}
+ {label for=repositoryPassword}{lang}Password{/lang}{/label}
+ {password_field name='repository[password]' value=$repository_data.password id=repositoryPassword}
+{/wrap}
+</div>
+<div class="clear"></div>
+{if $logged_user->canSeePrivate()}
+ {wrap field=visibility}
+ {label for=repositoryVisibility}Visibility{/label}
+ {select_visibility name=repository[visibility] value=$repository_data.visibility project=$active_project}
+ {/wrap}
+{else}
+ <input type="hidden" name="repository[visibility]" value="1"/>
+{/if}
12 activecollab/application/modules/github/views/github/add.tpl
@@ -0,0 +1,12 @@
+{title}Add a repository{/title}
+{add_bread_crumb}Add a repository{/add_bread_crumb}
+
+<div id="repository_add">
+ {form action=$repository_add_url method=post ask_on_leave=yes autofocus=yes}
+ {include_template name=_repository_form module=github controller=github}
+
+ {wrap_buttons}
+ {submit}Submit{/submit}
+ {/wrap_buttons}
+ {/form}
+</div>
78 activecollab/application/modules/github/views/github/history.tpl
@@ -0,0 +1,78 @@
+{add_bread_crumb}{$active_repository->getName()} - Commit History{/add_bread_crumb}
+<div id="repository_history">
+ <p class="pagination top">
+ <span class="inner_pagination">
+ {lang}Page{/lang}: {$page} -
+ {if !is_null($prev_page)}
+ <a href="{$active_repository->getHistoryUrl()}&amp;page={$prev_page}&amp;branch_tag={$branch_tag}">Prev</a>,
+ {/if}
+ <a href="{$active_repository->getHistoryUrl()}&amp;page={$next_page}&amp;branch_tag={$branch_tag}">Next</a>
+ </span>
+ </p>
+ <form action="{$active_repository->getHistoryUrl()}" method="get">
+ <p class="top" style="float:right;-moz-box-shadow: 1px 1px 1px #DEDEDE;border: 1px solid #DEDEDE;border-radius:5px;padding:5px 20px;">
+ <span style="color:#333;">
+ <strong>Branch/Tag: </strong>
+ <select style="font-size:10px;margin-left:7px;" name="branch_tag">
+ <optgroup label="Branches">
+ {if !is_null($branches)}
+ {foreach from=$branches item=hash key=name}
+ <option value="{$name}" {if $name == $branch_tag}selected="selected"{/if}>{$name}</option>
+ {/foreach}
+ {/if}
+ </optgroup>
+ <optgroup label="Tags">
+ {if !is_null($tags)}
+ {foreach from=$tags item=tag key=name}
+ <option value="{$name}" {if $name == $branch_tag}selected="selected"{/if}>{$name}</option>
+ {/foreach}
+ {/if}
+ </optgroup>
+ </select>
+ <input type="hidden" name="path_info" value="{$path_info}" />
+ <input type="submit" value="Change" style="width:auto;font-size:9px;background-color:#f7f7f7" />
+ </span>
+ </p>
+ </form>
+ <div class="grouped_commits">
+ {foreach from=$commits item=commits_day key=date}
+ <div class="date_slip">
+ <span>{$date}</span>
+ </div>
+ <table class="commit_history_table common_table">
+ {foreach from=$commits_day name=commit_list item=commit}
+ <tr class="commit {cycle values='odd,even'}" data-pk="{$commit->id}">
+ <td class="revision_number">
+ <a href="https://github.com/{$user}/{$repo}/commit/{$commit->id}">
+ {$commit->short_id}
+ </a>
+ </td>
+ <td class="revision_user">
+ {if $commit->author->login == ''}
+ {$commit->author->name}
+ {else}
+ {$commit->author->login}
+ {/if}
+ </td>
+ <td class="revision_details">
+ <div class="commit_message">
+ {$commit->message|nl2br}
+ </div>
+ <div class="commit_files"></div>
+ </td>
+ <td class="file_toggle">
+ <a>Show Changes</a>
+ </td>
+ </tr>
+ {/foreach}
+ </table>
+ {foreachelse}
+ <p style="text-align:center;padding-top:25px;font-size:120%;color:#333;font-weight:bold">
+ There are no commits for this period in the repository.
+ </p>
+ {/foreach}
+ </div>
+</div>
+{literal}
+
+{/literal}
53 activecollab/application/modules/github/views/github/index.tpl
@@ -0,0 +1,53 @@
+{title}Github Version Control{/title}
+{add_bread_crumb}{lang}Github{/lang}{/add_bread_crumb}
+
+ <div id="repository_index" class="repository_listing">
+ {if is_foreachable($repositories)}
+ <table>
+ <tr>
+ <th></th>
+ <th>{lang}Repository Name{/lang}</th>
+ <th>{lang}Last Tag{/lang}</th>
+ <th>{lang}Last Commit{/lang}</th>
+ <th></th>
+ <th></th>
+ </tr>
+ {foreach from=$repositories item=repository}
+ <tr class="{cycle values='odd,even'}">
+ <td class="star">{object_star object=$repository user=$logged_user}</td>
+ <td class="name">
+ <strong>{object_link object=$repository}</strong>
+ <span class="block details">
+ <a href="{$repository->getUrl()|clean}">{$repository->getUrl()|clean}</a>
+ </span>
+ </td>
+ <td>
+ {if is_null($repository->last_tag)}
+ -
+ {else}
+ <a href="https://github.com/{$repository->getUserName()}/{$repository->getRepoName()}/tree/{$repository->last_tag}">
+ {$repository->last_tag}
+ </a>
+ {/if}
+ </td>
+ <td class="last_commit" style="text-align:left;">
+ {if is_null($repository->last_commit)}
+ -
+ {else}
+ <a href="https://github.com{$repository->last_commit->url}">
+ {$repository->last_commit->id}
+ </a>
+ <br />{$repository->last_commit->author->name} on {$repository->last_commit->committed_date|date}
+ <br />
+ {$repository->last_commit->message}
+ {/if}
+ </td>
+ <td class="star">{object_subscription object=$repository user=$logged_user}</td>
+ <td class="visibility">{object_visibility object=$repository user=$logged_user}</td>
+ </tr>
+ {/foreach}
+ </table>
+ {else}
+ <p class="empty_page">{lang add_url=$add_repository_url}There are no repositories added. Would you like to <a href=":add_url">create one</a>{/lang}?</p>
+ {/if}
+ </div>
BIN  public/assets/modules/github/images/icon_medium.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 public/assets/modules/github/javascript/main.js
@@ -0,0 +1,72 @@
+$().ready(function() {
+ (function() {
+ $('#repository_history .commit_history_table tr')
+ .each(function(count, row) {
+ var row = $(row);
+ var commit_id = $(row).attr('data-pk'),
+ toggle = $(row).find('.file_toggle > a'),
+ files_div = $(row).find('.commit_files');
+
+ files_div.hide();
+
+ if(commit_id != '') {
+ toggle
+ .click(function() {
+ if(toggle.text() == 'Show Changes') {
+ files_div.show();
+ toggle.text('Hide Changes');
+ if(files_div.text() == '') {
+ files_div.append('<img src="' + App.data.indicator_url + '" />');
+ $.getJSON(App.data.commit_filepath_url,
+ {commit_id:commit_id},
+ function(data, status, xhr) {
+ var mod_html, add_html, del_html, err_html = '';
+
+ files_div.find('img').remove();
+
+ if(data.errors != undefined) {
+ err_html = '<p><strong>Errors Occurred:</strong></p><ul>'
+ $.each(data.errors, function(_count, error) {
+ err_html += '<li>' + error + '</li>';
+ });
+ err_html += '</ul>';
+ file_div.append(err_html)
+ } else {
+
+ if(data.modified != undefined && data.modified.length > 0) {
+ mod_html = '<ul class="action_group"><li><span class="action modified">modified</span><ul>';
+ $.each(data.modified, function(_count, path) {
+ mod_html += '<li>' + path + '</li>';
+ });
+ mod_html += '</ul></li></ul>';
+ }
+ if(data.added != undefined && data.added.length > 0) {
+ add_html = '<ul class="action_group"><li><span class="action added">added</span><ul>';
+ $.each(data.added, function(_count, path) {
+ add_html += '<li>' + path + '</li>';
+ });
+ add_html += '</ul></li></ul>';
+ }
+ if(data.removed != undefined && data.removed.length > 0) {
+ del_html = '<ul class="action_group"><li><span class="action removed">removed</span><ul>';
+ $.each(data.removed, function(_count, path) {
+ del_html += '<li>' + path + '</li>';
+ });
+ del_html += '</ul></li></ul>';
+ }
+
+ files_div.append(mod_html);
+ files_div.append(add_html);
+ files_div.append(del_html);
+ }
+ });
+ }
+ } else {
+ files_div.hide();
+ toggle.text('Show Changes');
+ }
+ });
+ }
+ });
+ })();
+})
2  public/assets/modules/github/stylesheets/main.css
@@ -0,0 +1,2 @@
+.file_toggle {text-align:right;width:90px;}
+.file_toggle > a:hover {cursor:pointer;}
Please sign in to comment.
Something went wrong with that request. Please try again.