├── app
├── commands
│ └── .gitkeep
├── config
│ ├── packages
│ │ ├── .gitkeep
│ │ └── barryvdh
│ │ │ └── laravel-cors
│ │ │ ├── .gitkeep
│ │ │ └── config.php
│ ├── local
│ │ └── .gitignore
│ ├── compile.php
│ ├── testing
│ │ ├── cache.php
│ │ └── session.php
│ ├── git-pretty-stats.php
│ ├── workbench.php
│ ├── view.php
│ ├── remote.php
│ ├── auth.php
│ ├── queue.php
│ ├── cache.php
│ ├── database.php
│ ├── mail.php
│ ├── session.php
│ └── app.php
├── controllers
│ ├── .gitkeep
│ ├── MainController.php
│ └── RepositoryController.php
├── database
│ ├── seeds
│ │ ├── .gitkeep
│ │ └── DatabaseSeeder.php
│ └── migrations
│ │ └── .gitkeep
├── start
│ ├── local.php
│ ├── artisan.php
│ └── global.php
├── storage
│ ├── .gitignore
│ ├── cache
│ │ └── .gitignore
│ ├── logs
│ │ └── .gitignore
│ ├── meta
│ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
├── views
│ ├── error
│ │ ├── git.blade.php
│ │ ├── datetimezone.blade.php
│ │ └── _layout.blade.php
│ └── index.php
├── assets
│ ├── fonts
│ │ ├── opensans-bold-webfont.eot
│ │ ├── opensans-bold-webfont.ttf
│ │ ├── opensans-bold-webfont.woff
│ │ ├── opensans-light-webfont.eot
│ │ ├── opensans-light-webfont.ttf
│ │ ├── opensans-bold-webfont.woff2
│ │ ├── opensans-italic-webfont.eot
│ │ ├── opensans-italic-webfont.ttf
│ │ ├── opensans-italic-webfont.woff
│ │ ├── opensans-italic-webfont.woff2
│ │ ├── opensans-light-webfont.woff
│ │ ├── opensans-light-webfont.woff2
│ │ ├── opensans-regular-webfont.eot
│ │ ├── opensans-regular-webfont.ttf
│ │ ├── opensans-regular-webfont.woff
│ │ └── opensans-regular-webfont.woff2
│ ├── scripts
│ │ ├── controllers
│ │ │ ├── repositories.js
│ │ │ └── repository.js
│ │ ├── templates
│ │ │ ├── contributor.html
│ │ │ ├── statistics.html
│ │ │ └── repository_list.html
│ │ ├── directives
│ │ │ ├── statistics.js
│ │ │ ├── repository_list.js
│ │ │ ├── commits_by_day_chart.js
│ │ │ ├── commits_by_hour_chart.js
│ │ │ ├── commits_by_date_chart.js
│ │ │ └── contributor.js
│ │ ├── services
│ │ │ └── repository.js
│ │ └── app.js
│ ├── test
│ │ ├── runner.html
│ │ ├── spec
│ │ │ └── controllers
│ │ │ │ └── main.coffee
│ │ └── .jshintrc
│ ├── views
│ │ ├── main.html
│ │ └── repository.html
│ └── styles
│ │ ├── _fonts.scss
│ │ ├── bootstrap.scss
│ │ ├── main.scss
│ │ └── _bootstrap_variables.scss
├── routes.php
├── GitPrettyStats
│ ├── Charts
│ │ ├── AbstractChart.php
│ │ ├── CommitsByDayChart.php
│ │ ├── CommitsByHourChart.php
│ │ ├── CommitsByDateChart.php
│ │ └── CommitsByContributorChart.php
│ ├── Providers
│ │ └── RepositoryFactoryServiceProvider.php
│ ├── StatisticsFormatter.php
│ ├── Repository.php
│ └── RepositoryFactory.php
├── lang
│ └── en
│ │ ├── pagination.php
│ │ ├── reminders.php
│ │ └── validation.php
├── tests
│ ├── TestCase.php
│ └── GitPrettyStats
│ │ ├── RepositoryTest.php
│ │ └── RepositoryFactoryTest.php
└── filters.php
├── repositories
└── .gitignore
├── .bowerrc
├── public
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ ├── opensans-bold-webfont.eot
│ ├── opensans-bold-webfont.ttf
│ ├── opensans-bold-webfont.woff
│ ├── opensans-bold-webfont.woff2
│ ├── opensans-italic-webfont.eot
│ ├── opensans-italic-webfont.ttf
│ ├── opensans-italic-webfont.woff
│ ├── opensans-light-webfont.eot
│ ├── opensans-light-webfont.ttf
│ ├── opensans-light-webfont.woff
│ ├── opensans-light-webfont.woff2
│ ├── opensans-regular-webfont.eot
│ ├── opensans-regular-webfont.ttf
│ ├── opensans-italic-webfont.woff2
│ ├── opensans-regular-webfont.woff
│ └── opensans-regular-webfont.woff2
├── .htaccess
├── index.php
└── scripts
│ ├── templates.js
│ └── app.js
├── .travis.yml
├── CONTRIBUTORS.md
├── .editorconfig
├── server.php
├── phpunit.xml.dist
├── bower.json
├── package.json
├── .gitignore
├── LICENSE.txt
├── composer.json
├── bootstrap
├── paths.php
├── start.php
└── autoload.php
├── .jshintrc
├── artisan
├── gulpfile.js
└── README.md
/app/commands/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/config/packages/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/database/seeds/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/database/migrations/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/start/local.php:
--------------------------------------------------------------------------------
1 |
2 | Options -MultiViews
3 |
4 | RewriteEngine On
5 | RewriteCond %{REQUEST_FILENAME} !-f
6 | RewriteRule ^ index.php [QSA,L]
7 |
8 |
--------------------------------------------------------------------------------
/app/routes.php:
--------------------------------------------------------------------------------
1 | date.timezone in your PHP ini file
5 | @stop
6 |
--------------------------------------------------------------------------------
/app/views/error/_layout.blade.php:
--------------------------------------------------------------------------------
1 | @extends ('_layout')
2 |
3 |
4 |
5 |
Error...
6 |
7 | @yield('error')
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/controllers/MainController.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.3
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | notifications:
8 | email:
9 | on_success: never
10 | on_failure: always
11 | before_script:
12 | - composer install --dev
13 | - composer dump-autoload -o
14 | script: phpunit --coverage-text
15 |
--------------------------------------------------------------------------------
/app/database/seeds/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call('UserTableSeeder');
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/app/assets/scripts/templates/contributor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ contributor.name }}
{{ contributor.email }}
4 |
{{ contributor.commits }}
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/assets/scripts/directives/statistics.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .directive('gpsStatistics', function() {
7 | return {
8 | restrict: 'E',
9 | templateUrl: 'statistics.html'
10 | };
11 | });
12 |
13 | })();
14 |
--------------------------------------------------------------------------------
/app/assets/scripts/controllers/repository.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .controller('RepositoryController', function($scope, repositories, repo) {
7 | $scope.repository = repo.data;
8 | $scope.repositories = repositories.data;
9 | $scope.charts = $scope.repository.data.charts;
10 | });
11 |
12 | }());
13 |
--------------------------------------------------------------------------------
/app/assets/scripts/templates/statistics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{ statistic.title }}
7 |
{{ statistic.value }}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | # Contributions to the Git Pretty Stats project
2 |
3 | ## Creator & Maintainer
4 |
5 | * Niklas Modess
6 |
7 | ## Contributors
8 |
9 | In chronological order:
10 |
11 | * filp
12 |
13 | * jasonrm
14 |
15 | * dreuter
16 |
17 | * [Your name or handle] <[email or website]>
18 |
--------------------------------------------------------------------------------
/app/assets/scripts/directives/repository_list.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .directive('gpsRepositoryList', function() {
7 | return {
8 | restrict: 'E',
9 | templateUrl: 'repository_list.html',
10 | scope: {
11 | 'repositories': '=',
12 | 'inline': '@'
13 | }
14 | };
15 | });
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/app/assets/scripts/templates/repository_list.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/app/assets/views/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/app/start/artisan.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
15 | }
16 |
17 | /**
18 | * @return Repository
19 | */
20 | public function getRepository()
21 | {
22 | return $this->repository;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.php]
21 | indent_size = 4
22 |
23 | [app/views/**/*.php]
24 | indent_size = 2
25 |
--------------------------------------------------------------------------------
/app/GitPrettyStats/Providers/RepositoryFactoryServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->bind('RepositoryFactory', function () {
16 | return new RepositoryFactory;
17 | });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/config/compile.php:
--------------------------------------------------------------------------------
1 |
4 |
5 | # load the controller's module
6 | beforeEach module 'assetsApp'
7 |
8 | MainCtrl = {}
9 | scope = {}
10 |
11 | # Initialize the controller and a mock scope
12 | beforeEach inject ($controller, $rootScope) ->
13 | scope = $rootScope.$new()
14 | MainCtrl = $controller 'MainCtrl', {
15 | $scope: scope
16 | }
17 |
18 | it 'should attach a list of awesomeThings to the scope', () ->
19 | expect(scope.awesomeThings.length).toBe 3
20 |
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 |
18 | 'next' => 'Next »',
19 |
20 | );
--------------------------------------------------------------------------------
/app/assets/scripts/services/repository.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .factory('Repository', function($http) {
7 | var repository = {};
8 |
9 | repository.all = function() {
10 | return $http({
11 | method: 'GET',
12 | url: base_url + '/repository/'
13 | });
14 | };
15 |
16 | repository.get = function(name) {
17 | return $http({
18 | method: 'GET',
19 | url: base_url + '/repository/' + name
20 | });
21 | };
22 |
23 | return repository;
24 | });
25 |
26 | })();
27 |
--------------------------------------------------------------------------------
/app/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | 'array',
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/app/config/testing/session.php:
--------------------------------------------------------------------------------
1 | 'array',
20 |
21 | );
22 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./app/tests/
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/GitPrettyStats/Charts/CommitsByDayChart.php:
--------------------------------------------------------------------------------
1 | getRepository()->getStatistics();
13 | $commitsByDay = $statistics['day']->getItems();
14 |
15 | $data = array();
16 | $days = array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
17 |
18 | foreach ($commitsByDay as $weekday => $commits) {
19 | $data[] = array($days[$weekday - 1], count($commits));
20 | }
21 |
22 | return $data;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/GitPrettyStats/Charts/CommitsByHourChart.php:
--------------------------------------------------------------------------------
1 | array(),
14 | 'y' => array()
15 | );
16 |
17 |
18 | $statistics = $this->getRepository()->getStatistics();
19 | $commitsByHour = $statistics['hour']->getItems();
20 |
21 | foreach ($commitsByHour as $hour => $commits) {
22 | $data['x'][] = $hour;
23 | $data['y'][] = count($commits);
24 | }
25 |
26 | return $data;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "git-pretty-stats",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "1.2.6",
6 | "json3": "~3.2.6",
7 | "es5-shim": "~2.1.0",
8 | "jquery": "~1.10.2",
9 | "sass-bootstrap": "~3.0.2",
10 | "angular-resource": "1.2.6",
11 | "angular-cookies": "1.2.6",
12 | "angular-sanitize": "1.2.6",
13 | "angular-route": "1.2.6",
14 | "angular-loading-bar": "~0.4.0",
15 | "highcharts": "https://github.com/highslide-software/highcharts.com.git#~3.0.9",
16 | "font-awesome": "~4.1.0",
17 | "angular-ui-router": "~0.2.10",
18 | "angular-snap": "~1.5.0"
19 | },
20 | "devDependencies": {
21 | "angular-mocks": "1.2.6",
22 | "angular-scenario": "1.2.6"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/config/git-pretty-stats.php:
--------------------------------------------------------------------------------
1 | array(
8 | * '/path/to/repository',
9 | * '../another/repo',
10 | * );
11 | *
12 | * 'emailAliases' in an array to map email addresses to another one so they end up under the same contributor:
13 | * 'emailAliases' => array(
14 | * 'an_old_email@example.com' => 'my_new_email@example.com',
15 | * 'annother_old_email@example.com' => 'my_new_email@example.com'
16 | * );
17 | */
18 |
19 | return array(
20 |
21 | 'repositoriesPath' => 'repositories',
22 |
23 | 'emailAliases' => array(),
24 |
25 | );
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "assets",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {
6 | "del": "^1.1.1",
7 | "gulp": "^3.8.11",
8 | "gulp-angular-filesort": "^1.1.1",
9 | "gulp-angular-templatecache": "^1.6.0",
10 | "gulp-autoprefixer": "^2.1.0",
11 | "gulp-concat": "^2.5.2",
12 | "gulp-filter": "^2.0.2",
13 | "gulp-if": "^1.2.5",
14 | "gulp-jshint": "^1.10.0",
15 | "gulp-livereload": "^3.8.0",
16 | "gulp-ruby-sass": "^1.0.1",
17 | "gulp-watch": "^4.2.3",
18 | "jshint-stylish": "^1.0.1",
19 | "main-bower-files": "^2.6.2",
20 | "merge-stream": "^0.1.7"
21 | },
22 | "engines": {
23 | "node": ">=0.8.0"
24 | },
25 | "scripts": {
26 | "test": "grunt test"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/assets/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "after": false,
23 | "afterEach": false,
24 | "angular": false,
25 | "before": false,
26 | "beforeEach": false,
27 | "browser": false,
28 | "describe": false,
29 | "expect": false,
30 | "inject": false,
31 | "it": false,
32 | "jasmine": false,
33 | "spyOn": false
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/app/lang/en/reminders.php:
--------------------------------------------------------------------------------
1 | "Passwords must be at least six characters and match the confirmation.",
17 |
18 | "user" => "We can't find a user with that e-mail address.",
19 |
20 | "token" => "This password reset token is invalid.",
21 |
22 | "sent" => "Password reminder sent!",
23 |
24 | );
25 |
--------------------------------------------------------------------------------
/app/filters.php:
--------------------------------------------------------------------------------
1 | find('git', '/usr/bin/git');
23 | if (!is_executable($gitExecutable)) {
24 | return View::make('error.git');
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/app/views/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Git Pretty Stats
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/config/workbench.php:
--------------------------------------------------------------------------------
1 | '',
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Workbench Author E-Mail Address
21 | |--------------------------------------------------------------------------
22 | |
23 | | Like the option above, your e-mail address is used when generating new
24 | | workbench packages. The e-mail is placed in your composer.json file
25 | | automatically after the package is created by the workbench tool.
26 | |
27 | */
28 |
29 | 'email' => '',
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/app/assets/scripts/directives/commits_by_day_chart.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .directive('gpsCommitsByDayChart', function($timeout) {
7 | return {
8 | restrict: 'E',
9 | replace: true,
10 | template: '',
11 |
12 | link: function(scope) {
13 | var data = scope.charts.day;
14 |
15 | $timeout(function() {
16 | $("#chart-commits-by-day").highcharts({
17 | chart: {
18 | type: "pie"
19 | },
20 | title: {
21 | text: ""
22 | },
23 | yAxis: {
24 | title: {
25 | text: ""
26 | }
27 | },
28 | series: [
29 | {
30 | name: "Commits",
31 | data: data
32 | }
33 | ]
34 | });
35 | }, 50);
36 | }
37 | };
38 | });
39 |
40 | })();
41 |
--------------------------------------------------------------------------------
/app/config/view.php:
--------------------------------------------------------------------------------
1 | array(__DIR__.'/../views'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Pagination View
21 | |--------------------------------------------------------------------------
22 | |
23 | | This view will be used to render the pagination link output, and can
24 | | be easily customized here to show any view you like. A clean view
25 | | compatible with Twitter's Bootstrap is given to you by default.
26 | |
27 | */
28 |
29 | 'pagination' => 'pagination::slider-3',
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ###################
2 | # Compiled source #
3 | ###################
4 | *.com
5 | *.class
6 | *.dll
7 | *.exe
8 | *.o
9 | *.so
10 |
11 | ############
12 | # Packages #
13 | ############
14 | # it's better to unpack these files and commit the raw source
15 | # git has its own built in compression methods
16 | *.7z
17 | *.dmg
18 | *.gz
19 | *.iso
20 | *.jar
21 | *.rar
22 | *.tar
23 | *.zip
24 |
25 | ######################
26 | # Logs and databases #
27 | ######################
28 | *.log
29 | *.sql
30 | *.sqlite
31 |
32 | ######################
33 | # OS generated files #
34 | ######################
35 | .DS_Store
36 | .DS_Store?
37 | ._*
38 | .Spotlight-V100
39 | .Trashes
40 | Icon?
41 | ehthumbs.db
42 | Thumbs.db
43 |
44 | #######
45 | # IDE #
46 | #######
47 | TAGS
48 | !TAGS/
49 | tags
50 | !tags/
51 | .*.s[a-w][a-z]
52 | *.un~
53 | Session.vim
54 | .netrwhist
55 | *~
56 | nbproject/
57 | .idea/
58 | *.sublime-workspace
59 |
60 | ###########
61 | # Project #
62 | ###########
63 | .sass-cache/
64 | .tmp/
65 | /vendor/
66 | /app/assets/bower_components
67 | /node_modules
68 | phpunit.xml
69 | composer.lock
70 | !.gitkeep
71 |
--------------------------------------------------------------------------------
/app/GitPrettyStats/Charts/CommitsByDateChart.php:
--------------------------------------------------------------------------------
1 | getRepository()->getFirstCommitDate());
13 | $end = new \DateTime($this->getRepository()->getLastCommitDate());
14 | $interval = \DateInterval::createFromDateString('1 day');
15 | $period = new \DatePeriod($begin, $interval, $end);
16 |
17 | $statistics = $this->getRepository()->getStatistics();
18 | $commitsByDate = $statistics['date']->getItems();
19 |
20 | $data = array(
21 | 'x' => array(),
22 | 'y' => array()
23 | );
24 |
25 | foreach ($period as $date) {
26 | $dayFormatted = $date->format("Y-m-d");
27 | $value = isset($commitsByDate[$dayFormatted]) ? count($commitsByDate[$dayFormatted]) : 0;
28 | $data['x'][] = $dayFormatted;
29 | $data['y'][] = $value;
30 | }
31 |
32 | return $data;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/assets/scripts/directives/commits_by_hour_chart.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .directive('gpsCommitsByHourChart', function($timeout) {
7 | return {
8 | restrict: 'E',
9 | replace: true,
10 | template: '',
11 |
12 | link: function(scope) {
13 | var data = scope.charts.hour;
14 |
15 | $timeout(function() {
16 | $("#chart-commits-by-hour").highcharts({
17 | chart: {
18 | type: "bar"
19 | },
20 | title: {
21 | text: ""
22 | },
23 | xAxis: {
24 | categories: data.x
25 | },
26 | yAxis: {
27 | title: {
28 | text: ""
29 | }
30 | },
31 | series: [
32 | {
33 | name: "Commits",
34 | data: data.y
35 | }
36 | ]
37 | });
38 | }, 50);
39 | }
40 | };
41 | });
42 |
43 | })();
44 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | This is the MIT license: http://www.opensource.org/licenses/mit-license.php
2 |
3 | Copyright 2013 Niklas Modess and contributors (see CONTRIBUTORS.md)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 | software and associated documentation files (the "Software"), to deal in the Software
7 | without restriction, including without limitation the rights to use, copy, modify, merge,
8 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
9 | to whom the Software is furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all copies or
12 | substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
17 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 | DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "laravel/framework": "4.1.*",
4 | "symfony/process": "v2.4.2",
5 | "barryvdh/laravel-cors": "v0.1.2",
6 | "klaussilveira/gitter": "dev-master"
7 | },
8 | "require-dev": {
9 | "phpunit/phpunit": "3.7.*@dev",
10 | "mockery/mockery": "0.9.4"
11 | },
12 | "repositories": [
13 | {
14 | "type": "git",
15 | "url": "https://github.com/modess/gitter"
16 | }
17 | ],
18 | "autoload": {
19 | "classmap": [
20 | "app/commands",
21 | "app/controllers",
22 | "app/GitPrettyStats",
23 | "app/database/migrations",
24 | "app/database/seeds",
25 | "app/tests/TestCase.php"
26 | ]
27 | },
28 | "scripts": {
29 | "post-install-cmd": [
30 | "php artisan optimize"
31 | ],
32 | "post-update-cmd": [
33 | "php artisan clear-compiled",
34 | "php artisan optimize"
35 | ],
36 | "post-create-project-cmd": [
37 | "php artisan key:generate"
38 | ]
39 | },
40 | "config": {
41 | "preferred-install": "dist"
42 | },
43 | "minimum-stability": "stable"
44 | }
45 |
--------------------------------------------------------------------------------
/app/config/packages/barryvdh/laravel-cors/config.php:
--------------------------------------------------------------------------------
1 | array(
21 | 'allow_credentials' => false,
22 | 'allow_origin'=> array(),
23 | 'allow_headers'=> array(),
24 | 'allow_methods'=> array(),
25 | 'expose_headers'=> array(),
26 | 'max_age' => 0
27 | ),
28 |
29 | 'paths' => array(
30 | '^/' => array(
31 | 'allow_origin'=> array('*'),
32 | 'allow_headers'=> array('Content-Type'),
33 | 'allow_methods'=> array('POST', 'PUT', 'GET', 'DELETE'),
34 | 'max_age' => 3600
35 | )
36 | ),
37 |
38 | );
39 |
--------------------------------------------------------------------------------
/app/controllers/RepositoryController.php:
--------------------------------------------------------------------------------
1 | factory = $factory;
20 | }
21 |
22 | /**
23 | * View for list
24 | *
25 | * @return View
26 | */
27 | public function all ()
28 | {
29 | $repositories = $this->factory->toArray();
30 |
31 | return Response::json($repositories);
32 | }
33 |
34 | /**
35 | * Get repository data
36 | *
37 | * @param str $name
38 | * @return Response
39 | */
40 | public function show ($name)
41 | {
42 | $this->factory->toArray(true);
43 |
44 | $repository = $this->factory->fromName($name);
45 |
46 | return Response::json(array(
47 | 'name' => $repository->getName(),
48 | 'branch' => $repository->getCurrentBranch(),
49 | 'data' => $repository->statistics()
50 | ));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/assets/scripts/app.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats', [
6 | 'gitPrettyStats.templates',
7 | 'ui.router',
8 | 'snap',
9 | 'chieffancypants.loadingBar'
10 | ])
11 | .run(function($rootScope, $state, $stateParams, snapRemote) {
12 | $rootScope.$state = $state;
13 | $rootScope.$stateParams = $stateParams;
14 |
15 | $rootScope.$on('$locationChangeStart', function() {
16 | snapRemote.close();
17 | });
18 | })
19 | .config(function($stateProvider, $urlRouterProvider, snapRemoteProvider) {
20 | snapRemoteProvider.globalOptions.touchToDrag = false;
21 | $urlRouterProvider.otherwise("/repositories");
22 |
23 | $stateProvider
24 | .state('repositories', {
25 | url: '/repositories',
26 | templateUrl: 'main.html',
27 | controller: 'RepositoriesController',
28 | resolve: {
29 | repositories: function(Repository) {
30 | return Repository.all();
31 | }
32 | }
33 | })
34 | .state('repository', {
35 | parent: 'repositories',
36 | url: '/:name',
37 | templateUrl: 'repository.html',
38 | controller: 'RepositoryController',
39 | resolve: {
40 | repo: function($stateParams, Repository) {
41 | return Repository.get($stateParams.name);
42 | }
43 | }
44 | });
45 | });
46 |
47 | }());
48 |
--------------------------------------------------------------------------------
/app/assets/scripts/directives/commits_by_date_chart.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .directive('gpsCommitsByDateChart', function($timeout) {
7 | return {
8 | restrict: 'E',
9 | replace: true,
10 | template: '',
11 |
12 | link: function(scope) {
13 | var data = scope.charts.date;
14 |
15 | $timeout(function() {
16 | $('#chart-commits-by-date').highcharts({
17 | chart: {
18 | type: 'areaspline',
19 | zoomType: 'x'
20 | },
21 | title: {
22 | text: ''
23 | },
24 | plotOptions: {
25 | series: {
26 | lineWidth: 1,
27 | marker: {
28 | enabled: false
29 | }
30 | }
31 | },
32 | xAxis: {
33 | categories: data.x,
34 | tickInterval: parseInt(data.x.length / 20),
35 | labels: {
36 | rotation: -45,
37 | y: 35
38 | }
39 | },
40 | yAxis: {
41 | title: {
42 | text: ''
43 | }
44 | },
45 | series: [
46 | {
47 | name: 'Commits',
48 | data: data.y
49 | }
50 | ]
51 | });
52 | }, 50);
53 | }
54 | };
55 | });
56 |
57 | })();
58 |
--------------------------------------------------------------------------------
/app/assets/scripts/directives/contributor.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats')
6 | .directive('gpsContributor', function($timeout) {
7 | return {
8 | restrict: 'E',
9 | replace: true,
10 | templateUrl: 'contributor.html',
11 | scope: {
12 | contributor: '='
13 | },
14 |
15 | link: function(scope, iElement) {
16 | $timeout(function() {
17 | $(iElement).find('.chart').highcharts({
18 | chart: {
19 | type: "areaspline",
20 | zoomType: "x"
21 | },
22 | title: {
23 | text: ""
24 | },
25 | plotOptions: {
26 | series: {
27 | lineWidth: 1,
28 | marker: {
29 | enabled: false
30 | }
31 | }
32 | },
33 | xAxis: {
34 | categories: scope.contributor.data.x,
35 | tickInterval: parseInt(scope.contributor.data.x.length / 10),
36 | labels: {
37 | rotation: -45,
38 | y: 35
39 | }
40 | },
41 | yAxis: {
42 | title: {
43 | text: ""
44 | }
45 | },
46 | series: [
47 | {
48 | name: "Commits",
49 | data: scope.contributor.data.y
50 | }
51 | ]
52 | });
53 | }, 50);
54 | }
55 | };
56 | });
57 |
58 | })();
59 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | /*
10 | |--------------------------------------------------------------------------
11 | | Register The Auto Loader
12 | |--------------------------------------------------------------------------
13 | |
14 | | Composer provides a convenient, automatically generated class loader
15 | | for our application. We just need to utilize it! We'll require it
16 | | into the script here so that we do not have to worry about the
17 | | loading of any our classes "manually". Feels great to relax.
18 | |
19 | */
20 |
21 | require __DIR__.'/../bootstrap/autoload.php';
22 |
23 | /*
24 | |--------------------------------------------------------------------------
25 | | Turn On The Lights
26 | |--------------------------------------------------------------------------
27 | |
28 | | We need to illuminate PHP development, so let's turn on the lights.
29 | | This bootstraps the framework and gets it ready for use, then it
30 | | will load up this application so that we can run it and send
31 | | the responses back to the browser and delight these users.
32 | |
33 | */
34 |
35 | $app = require_once __DIR__.'/../bootstrap/start.php';
36 |
37 | /*
38 | |--------------------------------------------------------------------------
39 | | Run The Application
40 | |--------------------------------------------------------------------------
41 | |
42 | | Once we have the application, we can simply call the run method,
43 | | which will execute the request and send the response back to
44 | | the client's browser allowing them to enjoy the creative
45 | | and wonderful application we have whipped up for them.
46 | |
47 | */
48 |
49 | $app->run();
50 |
--------------------------------------------------------------------------------
/app/config/remote.php:
--------------------------------------------------------------------------------
1 | 'production',
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Remote Server Connections
21 | |--------------------------------------------------------------------------
22 | |
23 | | These are the servers that will be accessible via the SSH task runner
24 | | facilities of Laravel. This feature radically simplifies executing
25 | | tasks on your servers, such as deploying out these applications.
26 | |
27 | */
28 |
29 | 'connections' => array(
30 |
31 | 'production' => array(
32 | 'host' => '',
33 | 'username' => '',
34 | 'password' => '',
35 | 'key' => '',
36 | 'keyphrase' => '',
37 | 'root' => '/var/www',
38 | ),
39 |
40 | ),
41 |
42 | /*
43 | |--------------------------------------------------------------------------
44 | | Remote Server Groups
45 | |--------------------------------------------------------------------------
46 | |
47 | | Here you may list connections under a single group name, which allows
48 | | you to easily access all of the servers at once using a short name
49 | | that is extremely easy to remember, such as "web" or "database".
50 | |
51 | */
52 |
53 | 'groups' => array(
54 |
55 | 'web' => array('production')
56 |
57 | ),
58 |
59 | );
60 |
--------------------------------------------------------------------------------
/bootstrap/paths.php:
--------------------------------------------------------------------------------
1 | __DIR__.'/../app',
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Public Path
21 | |--------------------------------------------------------------------------
22 | |
23 | | The public path contains the assets for your web application, such as
24 | | your JavaScript and CSS files, and also contains the primary entry
25 | | point for web requests into these applications from the outside.
26 | |
27 | */
28 |
29 | 'public' => __DIR__.'/../public',
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Base Path
34 | |--------------------------------------------------------------------------
35 | |
36 | | The base path is the root of the Laravel installation. Most likely you
37 | | will not need to change this value. But, if for some wild reason it
38 | | is necessary you will do so here, just proceed with some caution.
39 | |
40 | */
41 |
42 | 'base' => __DIR__.'/..',
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Storage Path
47 | |--------------------------------------------------------------------------
48 | |
49 | | The storage path is used by Laravel to store cached Blade views, logs
50 | | and other pieces of information. You may modify the path here when
51 | | you want to change the location of this directory for your apps.
52 | |
53 | */
54 |
55 | 'storage' => __DIR__.'/../app/storage',
56 |
57 | );
58 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "maxerr" : 50,
3 |
4 | "bitwise" : true,
5 | "camelcase" : false,
6 | "curly" : true,
7 | "eqeqeq" : true,
8 | "forin" : true,
9 | "freeze" : true,
10 | "immed" : false,
11 | "indent" : 2,
12 | "latedef" : false,
13 | "newcap" : false,
14 | "noarg" : true,
15 | "noempty" : true,
16 | "nonbsp" : true,
17 | "nonew" : false,
18 | "plusplus" : false,
19 | "quotmark" : false,
20 |
21 | "undef" : true,
22 | "unused" : true,
23 | "strict" : true,
24 | "maxparams" : false,
25 | "maxdepth" : false,
26 | "maxstatements" : false,
27 | "maxcomplexity" : false,
28 | "maxlen" : false,
29 |
30 | "asi" : false,
31 | "boss" : false,
32 | "debug" : false,
33 | "eqnull" : false,
34 | "es5" : false,
35 | "esnext" : false,
36 | "moz" : false,
37 |
38 | "evil" : false,
39 | "expr" : false,
40 | "funcscope" : false,
41 | "globalstrict" : false,
42 | "iterator" : false,
43 | "lastsemic" : false,
44 | "laxbreak" : false,
45 | "laxcomma" : false,
46 | "loopfunc" : false,
47 | "multistr" : false,
48 | "noyield" : false,
49 | "notypeof" : false,
50 | "proto" : false,
51 | "scripturl" : false,
52 | "shadow" : false,
53 | "sub" : false,
54 | "supernew" : false,
55 | "validthis" : false,
56 |
57 | "browser" : true,
58 | "browserify" : false,
59 | "couch" : false,
60 | "devel" : true,
61 | "dojo" : false,
62 | "jasmine" : true,
63 | "jquery" : true,
64 | "mocha" : true,
65 | "mootools" : false,
66 | "node" : false,
67 | "nonstandard" : false,
68 | "prototypejs" : false,
69 | "qunit" : false,
70 | "rhino" : false,
71 | "shelljs" : false,
72 | "worker" : false,
73 | "wsh" : false,
74 | "yui" : false,
75 |
76 | "predef" : [
77 | "angular"
78 | ],
79 |
80 | "globals" : {
81 | "base_url": true
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/assets/styles/_fonts.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'OpenSans-Bold';
3 | src: url('../fonts/opensans-bold-webfont.eot');
4 | src: url('../fonts/opensans-bold-webfont.eot?#iefix') format('embedded-opentype'),
5 | url('../fonts/opensans-bold-webfont.woff2') format('woff2'),
6 | url('../fonts/opensans-bold-webfont.woff') format('woff'),
7 | url('../fonts/opensans-bold-webfont.ttf') format('truetype'),
8 | url('../fonts/opensans-bold-webfont.svg#open_sansbold') format('svg');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | @font-face {
14 | font-family: 'OpenSans-Italic';
15 | src: url('../fonts/opensans-italic-webfont.eot');
16 | src: url('../fonts/opensans-italic-webfont.eot?#iefix') format('embedded-opentype'),
17 | url('../fonts/opensans-italic-webfont.woff2') format('woff2'),
18 | url('../fonts/opensans-italic-webfont.woff') format('woff'),
19 | url('../fonts/opensans-italic-webfont.ttf') format('truetype'),
20 | url('../fonts/opensans-italic-webfont.svg#open_sansitalic') format('svg');
21 | font-weight: normal;
22 | font-style: normal;
23 | }
24 |
25 | @font-face {
26 | font-family: 'OpenSans-Light';
27 | src: url('../fonts/opensans-light-webfont.eot');
28 | src: url('../fonts/opensans-light-webfont.eot?#iefix') format('embedded-opentype'),
29 | url('../fonts/opensans-light-webfont.woff2') format('woff2'),
30 | url('../fonts/opensans-light-webfont.woff') format('woff'),
31 | url('../fonts/opensans-light-webfont.ttf') format('truetype'),
32 | url('../fonts/opensans-light-webfont.svg#open_sanslight') format('svg');
33 | font-weight: normal;
34 | font-style: normal;
35 | }
36 |
37 | @font-face {
38 | font-family: 'OpenSans';
39 | src: url('../fonts/opensans-regular-webfont.eot');
40 | src: url('../fonts/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'),
41 | url('../fonts/opensans-regular-webfont.woff2') format('woff2'),
42 | url('../fonts/opensans-regular-webfont.woff') format('woff'),
43 | url('../fonts/opensans-regular-webfont.ttf') format('truetype'),
44 | url('../fonts/opensans-regular-webfont.svg#open_sansregular') format('svg');
45 | font-weight: normal;
46 | font-style: normal;
47 | }
48 |
--------------------------------------------------------------------------------
/app/assets/views/repository.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
31 |
By date
32 |
33 |
34 |
35 |
36 |
37 |
38 |
By day of week
39 |
40 |
41 |
42 |
43 |
44 |
By hour of day
45 |
46 |
47 |
48 |
49 |
50 |
53 |
Contributors (zoomable)
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/config/auth.php:
--------------------------------------------------------------------------------
1 | 'eloquent',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Authentication Model
23 | |--------------------------------------------------------------------------
24 | |
25 | | When using the "Eloquent" authentication driver, we need to know which
26 | | Eloquent model should be used to retrieve your users. Of course, it
27 | | is often just the "User" model but you may use whatever you like.
28 | |
29 | */
30 |
31 | 'model' => 'User',
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Authentication Table
36 | |--------------------------------------------------------------------------
37 | |
38 | | When using the "Database" authentication driver, we need to know which
39 | | table should be used to retrieve your users. We have chosen a basic
40 | | default value but you may easily change it to any table you like.
41 | |
42 | */
43 |
44 | 'table' => 'users',
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Password Reminder Settings
49 | |--------------------------------------------------------------------------
50 | |
51 | | Here you may set the settings for password reminders, including a view
52 | | that should be used as your password reminder e-mail. You will also
53 | | be able to set the name of the table that holds the reset tokens.
54 | |
55 | | The "expire" time is the number of minutes that the reminder should be
56 | | considered valid. This security feature keeps tokens short-lived so
57 | | they have less time to be guessed. You may change this as needed.
58 | |
59 | */
60 |
61 | 'reminder' => array(
62 |
63 | 'email' => 'emails.auth.reminder',
64 |
65 | 'table' => 'password_reminders',
66 |
67 | 'expire' => 60,
68 |
69 | ),
70 |
71 | );
72 |
--------------------------------------------------------------------------------
/bootstrap/start.php:
--------------------------------------------------------------------------------
1 | detectEnvironment(function () {
28 | return 'local';
29 | });
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Bind Paths
34 | |--------------------------------------------------------------------------
35 | |
36 | | Here we are binding the paths configured in paths.php to the app. You
37 | | should not be changing these here. If you need to change these you
38 | | may do so within the paths.php file and they will be bound here.
39 | |
40 | */
41 |
42 | $app->bindInstallPaths(require __DIR__.'/paths.php');
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Load The Application
47 | |--------------------------------------------------------------------------
48 | |
49 | | Here we will load this Illuminate application. We will keep this in a
50 | | separate location so we can isolate the creation of an application
51 | | from the actual running of the application with a given request.
52 | |
53 | */
54 |
55 | $framework = $app['path.base'].'/vendor/laravel/framework/src';
56 |
57 | require $framework.'/Illuminate/Foundation/start.php';
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | Return The Application
62 | |--------------------------------------------------------------------------
63 | |
64 | | This script returns the application instance. The instance is given to
65 | | the calling script so we can separate the building of the instances
66 | | from the actual running of the application and sending responses.
67 | |
68 | */
69 |
70 | return $app;
71 |
--------------------------------------------------------------------------------
/app/assets/styles/bootstrap.scss:
--------------------------------------------------------------------------------
1 | $icon-font-path: "/bower_components/sass-bootstrap/fonts/";
2 |
3 | // Core variables and mixins
4 | @import "bootstrap_variables";
5 | @import "../bower_components/sass-bootstrap/lib/mixins";
6 |
7 | // Reset
8 | @import "../bower_components/sass-bootstrap/lib/normalize";
9 | @import "../bower_components/sass-bootstrap/lib/print";
10 |
11 | // Core CSS
12 | @import "../bower_components/sass-bootstrap/lib/scaffolding";
13 | @import "../bower_components/sass-bootstrap/lib/type";
14 | @import "../bower_components/sass-bootstrap/lib/code";
15 | @import "../bower_components/sass-bootstrap/lib/grid";
16 | @import "../bower_components/sass-bootstrap/lib/tables";
17 | @import "../bower_components/sass-bootstrap/lib/forms";
18 | @import "../bower_components/sass-bootstrap/lib/buttons";
19 |
20 | // Components
21 | @import "../bower_components/sass-bootstrap/lib/component-animations";
22 | @import "../bower_components/sass-bootstrap/lib/glyphicons";
23 | @import "../bower_components/sass-bootstrap/lib/dropdowns";
24 | @import "../bower_components/sass-bootstrap/lib/button-groups";
25 | @import "../bower_components/sass-bootstrap/lib/input-groups";
26 | @import "../bower_components/sass-bootstrap/lib/navs";
27 | @import "../bower_components/sass-bootstrap/lib/navbar";
28 | @import "../bower_components/sass-bootstrap/lib/breadcrumbs";
29 | @import "../bower_components/sass-bootstrap/lib/pagination";
30 | @import "../bower_components/sass-bootstrap/lib/pager";
31 | @import "../bower_components/sass-bootstrap/lib/labels";
32 | @import "../bower_components/sass-bootstrap/lib/badges";
33 | @import "../bower_components/sass-bootstrap/lib/jumbotron";
34 | @import "../bower_components/sass-bootstrap/lib/thumbnails";
35 | @import "../bower_components/sass-bootstrap/lib/alerts";
36 | @import "../bower_components/sass-bootstrap/lib/progress-bars";
37 | @import "../bower_components/sass-bootstrap/lib/media";
38 | @import "../bower_components/sass-bootstrap/lib/list-group";
39 | @import "../bower_components/sass-bootstrap/lib/panels";
40 | @import "../bower_components/sass-bootstrap/lib/wells";
41 | @import "../bower_components/sass-bootstrap/lib/close";
42 |
43 | // Components w/ JavaScript
44 | @import "../bower_components/sass-bootstrap/lib/modals";
45 | @import "../bower_components/sass-bootstrap/lib/tooltip";
46 | @import "../bower_components/sass-bootstrap/lib/popovers";
47 | @import "../bower_components/sass-bootstrap/lib/carousel";
48 |
49 | // Utility classes
50 | @import "../bower_components/sass-bootstrap/lib/utilities";
51 | @import "../bower_components/sass-bootstrap/lib/responsive-utilities";
52 |
--------------------------------------------------------------------------------
/app/config/queue.php:
--------------------------------------------------------------------------------
1 | 'sync',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Queue Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may configure the connection information for each server that
26 | | is used by your application. A default configuration has been added
27 | | for each back-end shipped with Laravel. You are free to add more.
28 | |
29 | */
30 |
31 | 'connections' => array(
32 |
33 | 'sync' => array(
34 | 'driver' => 'sync',
35 | ),
36 |
37 | 'beanstalkd' => array(
38 | 'driver' => 'beanstalkd',
39 | 'host' => 'localhost',
40 | 'queue' => 'default',
41 | ),
42 |
43 | 'sqs' => array(
44 | 'driver' => 'sqs',
45 | 'key' => 'your-public-key',
46 | 'secret' => 'your-secret-key',
47 | 'queue' => 'your-queue-url',
48 | 'region' => 'us-east-1',
49 | ),
50 |
51 | 'iron' => array(
52 | 'driver' => 'iron',
53 | 'project' => 'your-project-id',
54 | 'token' => 'your-token',
55 | 'queue' => 'your-queue-name',
56 | ),
57 |
58 | 'redis' => array(
59 | 'driver' => 'redis',
60 | 'queue' => 'default',
61 | ),
62 |
63 | ),
64 |
65 | /*
66 | |--------------------------------------------------------------------------
67 | | Failed Queue Jobs
68 | |--------------------------------------------------------------------------
69 | |
70 | | These options configure the behavior of failed queue job logging so you
71 | | can control which database and table are used to store the jobs that
72 | | have failed. You may change them to any database / table you wish.
73 | |
74 | */
75 |
76 | 'failed' => array(
77 |
78 | 'database' => 'mysql', 'table' => 'failed_jobs',
79 |
80 | ),
81 |
82 | );
83 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | setRequestForConsoleEnvironment();
45 |
46 | $artisan = Illuminate\Console\Application::start($app);
47 |
48 | /*
49 | |--------------------------------------------------------------------------
50 | | Run The Artisan Application
51 | |--------------------------------------------------------------------------
52 | |
53 | | When we run the console application, the current CLI command will be
54 | | executed in this console and the response sent back to a terminal
55 | | or another output device for the developers. Here goes nothing!
56 | |
57 | */
58 |
59 | $status = $artisan->run();
60 |
61 | /*
62 | |--------------------------------------------------------------------------
63 | | Shutdown The Application
64 | |--------------------------------------------------------------------------
65 | |
66 | | Once Artisan has finished running. We will fire off the shutdown events
67 | | so that any final work may be done by the application before we shut
68 | | down the process. This is the last thing to happen to the request.
69 | |
70 | */
71 |
72 | $app->shutdown();
73 |
74 | exit($status);
--------------------------------------------------------------------------------
/app/GitPrettyStats/Charts/CommitsByContributorChart.php:
--------------------------------------------------------------------------------
1 | getRepository()->getStatistics();
15 | $commitsByContributor = $statistics['contributors']->getItems();
16 |
17 | foreach ($commitsByContributor as $contributor) {
18 | $contributorInfo = null;
19 | $begin = new \DateTime($this->getRepository()->getFirstCommitDate());
20 | $end = new \DateTime($this->getRepository()->getLastCommitDate());
21 | $interval = \DateInterval::createFromDateString('1 day');
22 | $period = new \DatePeriod($begin, $interval, $end);
23 |
24 | $commitsData = array();
25 | $totalCommits = 0;
26 |
27 | $contributorCommits = $contributor->getItems();
28 |
29 | foreach ($period as $date) {
30 | $dayFormatted = $date->format("Y-m-d");
31 |
32 | $value = isset($contributorCommits[$dayFormatted]) ? count($contributorCommits[$dayFormatted]) : 0;
33 |
34 | if ($value > 0 && $contributorInfo === null) {
35 | $commit = $contributorCommits[$dayFormatted][0];
36 | $contributorInfo = array(
37 | 'name' => $commit->getAuthor()->getName(),
38 | 'email' => $commit->getAuthor()->getEmail(),
39 | );
40 | }
41 |
42 | $totalCommits += $value;
43 |
44 | $commitsData['x'][] = $dayFormatted;
45 | $commitsData['y'][] = $value;
46 | }
47 |
48 | $data[] = array(
49 | 'name' => $contributorInfo['name'],
50 | 'email' => $contributorInfo['email'],
51 | 'commits' => $totalCommits,
52 | 'data' => $commitsData,
53 | );
54 | }
55 |
56 | usort($data, array($this, 'sortContributorsByCommits'));
57 |
58 | return $data;
59 | }
60 |
61 | /**
62 | * Sort contributors by number of commits they have in descending order
63 | *
64 | * @param array $sortA
65 | * @param array $sortB
66 | * @return bool
67 | */
68 | public function sortContributorsByCommits($sortA, $sortB)
69 | {
70 | if ($sortA['commits'] == $sortB['commits']) {
71 | return 0;
72 | }
73 |
74 | return ($sortA['commits'] > $sortB['commits']) ? -1 : 1;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/bootstrap/autoload.php:
--------------------------------------------------------------------------------
1 | 'file',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | File Cache Location
23 | |--------------------------------------------------------------------------
24 | |
25 | | When using the "file" cache driver, we need a location where the cache
26 | | files may be stored. A sensible default has been specified, but you
27 | | are free to change it to any other place on disk that you desire.
28 | |
29 | */
30 |
31 | 'path' => storage_path().'/cache',
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Database Cache Connection
36 | |--------------------------------------------------------------------------
37 | |
38 | | When using the "database" cache driver you may specify the connection
39 | | that should be used to store the cached items. When this option is
40 | | null the default database connection will be utilized for cache.
41 | |
42 | */
43 |
44 | 'connection' => null,
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Database Cache Table
49 | |--------------------------------------------------------------------------
50 | |
51 | | When using the "database" cache driver we need to know the table that
52 | | should be used to store the cached items. A default table name has
53 | | been provided but you're free to change it however you deem fit.
54 | |
55 | */
56 |
57 | 'table' => 'cache',
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | Memcached Servers
62 | |--------------------------------------------------------------------------
63 | |
64 | | Now you may specify an array of your Memcached servers that should be
65 | | used when utilizing the Memcached cache driver. All of the servers
66 | | should contain a value for "host", "port", and "weight" options.
67 | |
68 | */
69 |
70 | 'memcached' => array(
71 |
72 | array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100),
73 |
74 | ),
75 |
76 | /*
77 | |--------------------------------------------------------------------------
78 | | Cache Key Prefix
79 | |--------------------------------------------------------------------------
80 | |
81 | | When utilizing a RAM based store such as APC or Memcached, there might
82 | | be other applications utilizing the same cache. So, we'll specify a
83 | | value to get prefixed to all our keys so we can avoid collisions.
84 | |
85 | */
86 |
87 | 'prefix' => 'laravel',
88 |
89 | );
90 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp'),
4 | sass = require('gulp-ruby-sass'),
5 | concat = require('gulp-concat'),
6 | autoprefixer = require('gulp-autoprefixer'),
7 | mainBowerFiles = require('main-bower-files'),
8 | gulpFilter = require('gulp-filter'),
9 | merge = require('merge-stream'),
10 | watch = require('gulp-watch'),
11 | gulpif = require('gulp-if'),
12 | del = require('del'),
13 | livereload = require('gulp-livereload'),
14 | templateCache = require('gulp-angular-templatecache'),
15 | jshint = require('gulp-jshint'),
16 | jshintstylish = require('jshint-stylish'),
17 | angularFilesort = require('gulp-angular-filesort');
18 |
19 | var assetsFolder = './app/assets/';
20 | var assetsVendorFolder = assetsFolder + 'bower_components/';
21 | var assetsJsFolder = assetsFolder + 'scripts/';
22 | var assetsSassFolder = assetsFolder + 'styles/';
23 | var assetsViewsFolder = assetsFolder + 'views/';
24 | var assetsFontsFolder = assetsFolder + 'fonts/';
25 | var assetsTemplatesFolder = assetsJsFolder + 'templates/';
26 |
27 | var distFolder = './public/';
28 | var distJsFolder = distFolder + 'scripts/';
29 | var distCssFolder = distFolder + 'styles/';
30 | var distFontsFolder = distFolder + 'fonts/';
31 |
32 | gulp.task('styles', function() {
33 | return sass(assetsSassFolder, { sourcemap: true })
34 | .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1'))
35 | .pipe(concat('main.css'))
36 | .pipe(gulp.dest('public/styles'));
37 | });
38 |
39 | gulp.task('scripts', function() {
40 | return gulp.src(assetsJsFolder + '**/*.js')
41 | .pipe(angularFilesort())
42 | .pipe(concat('app.js'))
43 | .pipe(gulp.dest('public/scripts'));
44 | });
45 |
46 | gulp.task('fonts', function () {
47 | var fontAwesome = gulp.src(assetsVendorFolder + 'font-awesome/fonts/*');
48 | var openSans = gulp.src(assetsFontsFolder + '/*');
49 |
50 | return merge(fontAwesome, openSans)
51 | .pipe(gulp.dest(distFontsFolder));
52 | });
53 |
54 | gulp.task('jshint', function() {
55 | return gulp.src(assetsJsFolder + '**/*.js')
56 | .pipe(jshint('.jshintrc'))
57 | .pipe(jshint.reporter(jshintstylish));
58 | });
59 |
60 | gulp.task('clean', function (cb) {
61 | del([
62 | distFolder + '**/*',
63 | '!' + distFolder + 'index.php',
64 | '!' + distFolder + '.htaccess'
65 | ], cb);
66 | });
67 |
68 | gulp.task('angular-templates', function () {
69 | return gulp.src([assetsViewsFolder + '/**/*.html', assetsTemplatesFolder + '**/*.html'])
70 | .pipe(templateCache({
71 | module: 'gitPrettyStats.templates',
72 | standalone: true
73 | }))
74 | .pipe(gulp.dest('public/scripts'));
75 | });
76 |
77 | gulp.task('vendor', function() {
78 | var styles = gulp.src(mainBowerFiles())
79 | .pipe(gulpFilter('*.css'))
80 | .pipe(concat('vendor.css'))
81 | .pipe(gulp.dest(distCssFolder));
82 |
83 | var scripts = gulp.src(mainBowerFiles())
84 | .pipe(gulpFilter('*.js'))
85 | .pipe(concat('vendor.js'))
86 | .pipe(gulp.dest(distJsFolder));
87 |
88 | return merge(styles, scripts);
89 | });
90 |
91 | gulp.task('watch', ['default'], function () {
92 | livereload.listen();
93 |
94 | gulp.watch(assetsSassFolder + '**/*.scss', ['styles']);
95 | gulp.watch(assetsJsFolder + '**/*.js', ['scripts', 'jshint']);
96 | gulp.watch([assetsViewsFolder + '**/*.html', assetsTemplatesFolder + '**/*.html'], ['angular-templates']);
97 | gulp.watch(distFolder + '**/*').on('change', livereload.changed);
98 | });
99 |
100 | gulp.task('default', ['clean'], function () {
101 | gulp.start(['styles', 'scripts', 'vendor', 'fonts', 'angular-templates']);
102 | });
103 |
--------------------------------------------------------------------------------
/public/scripts/templates.js:
--------------------------------------------------------------------------------
1 | angular.module("gitPrettyStats.templates", []).run(["$templateCache", function($templateCache) {$templateCache.put("main.html","\n \n
\n\n\n\n\n");
2 | $templateCache.put("repository.html","\n\n\n
\n\n
\n
\n \n
\n\n
\n
By date
\n
\n\n
\n\n
\n
\n
By day of week
\n \n
\n \n\n
\n
By hour of day
\n \n \n
\n
\n\n
\n
Contributors (zoomable)
\n
\n \n
\n
\n
\n
\n");
3 | $templateCache.put("contributor.html","\n
\n
{{ contributor.name }}
{{ contributor.email }}
\n
{{ contributor.commits }}
\n
\n
\n
\n");
4 | $templateCache.put("repository_list.html","\n");
5 | $templateCache.put("statistics.html","\n\n\n
\n
\n
{{ statistic.title }}
\n
{{ statistic.value }}
\n
\n
\n
\n");}]);
--------------------------------------------------------------------------------
/app/GitPrettyStats/StatisticsFormatter.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class StatisticsFormatter
12 | {
13 | /**
14 | * @var Repository
15 | */
16 | protected $repository;
17 |
18 | /**
19 | * @var array Statistics
20 | */
21 | protected $statistics;
22 |
23 | /**
24 | * @param Repository $repository
25 | */
26 | public function __construct (Repository $repository)
27 | {
28 | $this->repository = $repository;
29 | }
30 |
31 | /**
32 | * Get certain type of statistics
33 | *
34 | * @param $type
35 | * @return mixed
36 | */
37 | public function statistics ($type)
38 | {
39 | if ($this->statistics === null) {
40 | $this->statistics = $this->repository->getStatistics();
41 | }
42 |
43 | return $this->statistics[$type];
44 | }
45 |
46 | /**
47 | * Statistics for number of contributors
48 | *
49 | * @return array
50 | */
51 | public function contributors ()
52 | {
53 | $contributors = $this->statistics('contributors');
54 |
55 | return array(
56 | 'title' => 'Total contributors',
57 | 'value' => number_format(count($contributors))
58 | );
59 | }
60 |
61 | /**
62 | * Statistics for number of commits
63 | *
64 | * @return array
65 | */
66 | public function totalNumberOfCommits ()
67 | {
68 | return array(
69 | 'title' => 'Total commits',
70 | 'value' => number_format($this->repository->getTotalCommits())
71 | );
72 | }
73 |
74 | /**
75 | * Statistics for number of average commits per contributor
76 | *
77 | * @return array
78 | */
79 | public function contributorsAverageCommits ()
80 | {
81 | $contributors = $this->statistics('contributors');
82 |
83 | return array(
84 | 'title' => 'Average commits per contributor',
85 | 'value' => number_format($this->repository->getTotalCommits() / count($contributors), 2)
86 | );
87 | }
88 |
89 | /**
90 | * Statistics for first commit date
91 | *
92 | * @return array
93 | */
94 | public function firstCommitDate ()
95 | {
96 | return array(
97 | 'title' => 'First commit date',
98 | 'value' => $this->repository->getFirstCommitDate(),
99 | );
100 | }
101 |
102 | /**
103 | * Statistics for latest commit date
104 | *
105 | * @return array
106 | */
107 | public function latestCommitDate ()
108 | {
109 | return array(
110 | 'title' => 'Latest commit date',
111 | 'value' => $this->repository->getLastCommitDate(),
112 | );
113 | }
114 |
115 | /**
116 | * Statistics for how long the repository has been active
117 | *
118 | * @return array
119 | */
120 | public function activeFor ()
121 | {
122 | $firstCommitDate = new Carbon($this->repository->getFirstCommitDate());
123 | $lastCommitDate = new Carbon($this->repository->getLastCommitDate());
124 |
125 | return array(
126 | 'title' => 'Active for',
127 | 'value' => number_format($firstCommitDate->diffInDays($lastCommitDate)) . " days"
128 | );
129 | }
130 |
131 | /**
132 | * Statistics for average commits per day
133 | *
134 | * @return array
135 | */
136 | public function averageCommitsPerDay ()
137 | {
138 | $commitsByDate = $this->statistics('date');
139 |
140 | return array(
141 | 'title' => 'Average commits per day',
142 | 'value' => number_format(count($commitsByDate) / $this->repository->getTotalCommits(), 2)
143 | );
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/app/GitPrettyStats/Repository.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Repository extends GitterRepository
19 | {
20 | /** @var \GitPrettyStats\StatisticsFormatter */
21 | public $statisticsFormatter;
22 |
23 | /** @var array Author email aliases */
24 | public $emailAliases = array();
25 |
26 | /**
27 | * Constructor
28 | *
29 | * @param string $path
30 | * @param Client $client Git client
31 | * @param StatisticsFormatter $statisticsFormatter
32 | * @return \GitPrettyStats\Repository
33 | */
34 | public function __construct($path, Client $client = null, StatisticsFormatter $statisticsFormatter = null)
35 | {
36 | $this->statisticsFormatter = $statisticsFormatter ?: new StatisticsFormatter($this);
37 |
38 | $emailAliases = Config::get('git-pretty-stats.emailAliases');
39 | $this->emailAliases = ($emailAliases && is_array($emailAliases)) ? $emailAliases : null;
40 |
41 | $client = ($client) ? $client : new Client;
42 | parent::__construct($path, $client);
43 | }
44 |
45 | /**
46 | * Get email alias
47 | *
48 | * @param string $email
49 | * @return string
50 | */
51 | public function getEmailAlias($email)
52 | {
53 | return isset($this->emailAliases[$email]) ? $this->emailAliases[$email] : $email;
54 | }
55 |
56 | /**
57 | * Returns array with statistics and graph data
58 | *
59 | * @return array
60 | */
61 | public function statistics()
62 | {
63 | $commitsByDateChart = new CommitsByDateChart($this);
64 | $commitsByHourChart = new CommitsByHourChart($this);
65 | $commitsByDayChart = new CommitsByDayChart($this);
66 | $commitsByContributorChart = new CommitsByContributorChart($this);
67 |
68 | return array(
69 | 'statistics' => array(
70 | $this->statisticsFormatter->totalNumberOfCommits(),
71 | $this->statisticsFormatter->contributors(),
72 | $this->statisticsFormatter->contributorsAverageCommits(),
73 | $this->statisticsFormatter->firstCommitDate(),
74 | $this->statisticsFormatter->latestCommitDate(),
75 | $this->statisticsFormatter->activeFor(),
76 | $this->statisticsFormatter->averageCommitsPerDay(),
77 | ),
78 | 'charts' => array(
79 | 'date' => $commitsByDateChart->toArray(),
80 | 'hour' => $commitsByHourChart->toArray(),
81 | 'day' => $commitsByDayChart->toArray(),
82 | 'contributor' => $commitsByContributorChart->toArray(),
83 | )
84 | );
85 | }
86 |
87 | /**
88 | * Get first commit date
89 | *
90 | * @return string
91 | */
92 | public function getFirstCommitDate ()
93 | {
94 | $statistics = $this->getStatistics();
95 | $commitsByDate = $statistics['date'];
96 | $commits = $commitsByDate->getItems();
97 | $firstDate = key(array_slice($commits, 0, 1));
98 |
99 | return $firstDate;
100 | }
101 |
102 | /**
103 | * Get last commit date
104 | *
105 | * @return string
106 | */
107 | public function getLastCommitDate ()
108 | {
109 | $statistics = $this->getStatistics();
110 | $commitsByDate = $statistics['date'];
111 | $commits = $commitsByDate->getItems();
112 | $lastDate = key(array_slice($commits, -1));
113 |
114 | return $lastDate;
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/app/tests/GitPrettyStats/RepositoryTest.php:
--------------------------------------------------------------------------------
1 | client = m::mock('\Gitter\Client');
34 |
35 | \Config::shouldReceive('get')
36 | ->once()
37 | ->with('git-pretty-stats.emailAliases')
38 | ->andReturn(array('author_1_another@email.com' => 'author_1@email.com'));
39 |
40 | $commits = array();
41 | foreach ($this->commits as $commit) {
42 | $commits[] = $this->createCommit($commit[0], $commit[1], $commit[2]);
43 | }
44 |
45 | $this->commits = $commits;
46 | }
47 |
48 | public function createCommit($name, $email, $date)
49 | {
50 | $author = m::mock('stdClass');
51 | $author
52 | ->shouldReceive('getName')
53 | ->zeroOrMoreTimes()
54 | ->andReturn($name)
55 | ->shouldReceive('getEmail')
56 | ->zeroOrMoreTimes()
57 | ->andReturn($email);
58 |
59 | $commit = m::mock('stdClass');
60 | $commit
61 | ->shouldReceive('getCommiterDate')
62 | ->zeroOrMoreTimes()
63 | ->andReturn(new Carbon($date))
64 | ->shouldReceive('getAuthor')
65 | ->zeroOrMoreTimes()
66 | ->andReturn($author);
67 |
68 | return $commit;
69 | }
70 |
71 | public function tearDown()
72 | {
73 | m::close();
74 | }
75 |
76 | public function createInstance()
77 | {
78 | $repo = new Repository('.', $this->client);
79 | $repo->commits = $this->commits;
80 |
81 | return $repo;
82 | }
83 |
84 | public function repositoryNames ()
85 | {
86 | return array(
87 | array('./some/git/repository', 'repository'),
88 | array('sample.git', 'sample.git'),
89 | array('../../yet-another-repository', 'yet-another-repository'),
90 | );
91 | }
92 |
93 | /**
94 | * @dataProvider repositoryNames
95 | */
96 | public function testGetName($path, $expected)
97 | {
98 | $repo = new Repository($path, $this->client);
99 |
100 | $this->assertEquals(
101 | $expected,
102 | $repo->getName(),
103 | 'Returned incorrect repository name'
104 | );
105 | }
106 |
107 |
108 | public function testGetClient ()
109 | {
110 | $repo = $this->createInstance();
111 |
112 | $this->assertEquals($this->client, $repo->getClient(), 'Invalid client returned');
113 | }
114 |
115 | public function testGetEmailAlias ()
116 | {
117 | $repo = $this->createInstance();
118 |
119 | $this->assertEquals(
120 | 'email_without@alias.com',
121 | $repo->getEmailAlias('email_without@alias.com'),
122 | 'E-mail without alias should be returned'
123 | );
124 |
125 | $this->assertEquals(
126 | 'author_1@email.com',
127 | $repo->getEmailAlias('author_1_another@email.com'),
128 | 'E-mail with alias should return original'
129 | );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/assets/styles/main.scss:
--------------------------------------------------------------------------------
1 | @import "bootstrap_variables";
2 | @import "fonts";
3 |
4 | @mixin respond-to($media) {
5 | @if $media == xs {
6 | @media (max-width: $screen-xs-max) { @content; }
7 | }
8 | @else if $media == sm {
9 | @media (min-width: $screen-sm-min) and (max-width: $screen-md-min) { @content; }
10 | }
11 | @else if $media == md {
12 | @media (min-width: $screen-md-min) and (max-width: $screen-lg-min) { @content; }
13 | }
14 | @else if $media == lg {
15 | @media (min-width: $screen-lg-min) { @content; }
16 | }
17 | }
18 |
19 | .no-margin-top {
20 | margin-top: 0;
21 | }
22 |
23 | .divider {
24 | margin: 10px 0;
25 | }
26 |
27 | * {
28 | outline: none;
29 | }
30 |
31 | $footer-height: 55px;
32 | html {
33 | position: relative;
34 | min-height: 100%;
35 | }
36 |
37 | body {
38 | margin: 0 0 $footer-height;
39 | padding-top: 20px;
40 | }
41 |
42 | .footer {
43 | width: 100%;
44 | height: $footer-height;
45 | padding: 10px;
46 | bottom: 0;
47 | background: $color-cyan-light;
48 | text-align: center;
49 | position: absolute;
50 | z-index: 999;
51 | @include respond-to(sm) { display: none; }
52 | @include respond-to(xs) { display: none; }
53 |
54 | a {
55 | color: $color-cyan-darker;
56 | font-size: 1.8em;
57 |
58 | &:hover { color: $color-cyan-dark; }
59 | }
60 | }
61 |
62 | .snap-content {
63 | background: $color-cyan;
64 | padding-bottom: ($footer-height + 20);
65 | }
66 |
67 | .snap-drawer {
68 | background: $color-cyan-dark;
69 | }
70 |
71 | .navbar-default {
72 | .navbar-toggle {
73 | float: left;
74 | margin-left: 15px;
75 | margin-right: 0;
76 | display: block;
77 | background: $color-cyan-lighter;
78 | border-color: $color-cyan-lighter;
79 | color: #fff;
80 |
81 | .icon-bar {
82 | background: $color-cyan-dark;
83 | }
84 |
85 | &:hover, &:focus {
86 | background: $color-cyan-lighter;
87 | .icon-bar {
88 | background: $color-cyan-light;
89 | }
90 | }
91 | }
92 | }
93 |
94 | #tabs {
95 | margin-top: 15px;
96 | }
97 | .nav li a {
98 | color: $color-cyan-lighter;
99 | cursor: pointer;
100 |
101 | &:hover, &:focus {
102 | color: #fff;
103 | background: $color-cyan-light;
104 | border-color: $color-cyan-light $color-cyan-light $color-cyan-dark $color-cyan-light;
105 | }
106 | }
107 |
108 | h1,h2,h3,h4,h5,h6 {
109 | small {
110 | font-family: $font-regular;
111 | color: $color-cyan-darker;
112 | }
113 | }
114 |
115 | .list-group {
116 | max-width: $screen-sm;
117 | margin: 0 auto;
118 | list-style: none;
119 | font-family: $font-bold;
120 |
121 | &.inline {
122 | margin: $footer-height auto;
123 | }
124 |
125 | .list-group-item {
126 | .badge {
127 | background-color: $color-cyan;
128 | }
129 | }
130 | }
131 |
132 | $loading-bar-height: 10px;
133 | #loading-bar .bar,
134 | #loading-bar .peg {
135 | height: $loading-bar-height;
136 | }
137 | #loading-bar-spinner {
138 | top: ($loading-bar-height + 10);
139 | }
140 |
141 | .thumbnail {
142 | position: relative;
143 | margin-top: 1em;
144 | border: 0;
145 | background: $color-cyan-dark;
146 |
147 | h4 {
148 | font-size: 1.1em;
149 | margin-top: 8px;
150 | margin-left: 8px;
151 | }
152 |
153 | h5 {
154 | position: absolute;
155 | right: 8px;
156 | margin-top: 8px;
157 | top: 0;
158 | padding: 10px;
159 | background: $color-cyan;
160 | color: white;
161 | font-size: 1.25em;
162 | border-radius: $border-radius-base;
163 | }
164 | }
165 |
166 | .statistics {
167 | color: white;
168 | font-family: $font-bold;
169 |
170 | .thumbnail {
171 | background: $color-blue;
172 | padding: 10px;
173 | border: 0;
174 |
175 | .title {
176 | font-size: 1em;
177 | }
178 |
179 | .value {
180 | font-size: 3em;
181 | line-height: 1em;
182 | padding-top: 10px;
183 | text-align: right;
184 | @include respond-to(xs) { text-align: left; }
185 | }
186 | }
187 | }
188 |
189 | .commit-activity {
190 | .chart {
191 | height: 400px;
192 | }
193 |
194 | .divide-responsive-charts {
195 | display: none;
196 |
197 | @include respond-to(md) { display: block; }
198 | }
199 | }
200 |
201 | strong {
202 | font-family: $font-bold;
203 | }
204 |
--------------------------------------------------------------------------------
/app/config/database.php:
--------------------------------------------------------------------------------
1 | PDO::FETCH_CLASS,
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Default Database Connection Name
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may specify which of the database connections below you wish
24 | | to use as your default connection for all database work. Of course
25 | | you may use many connections at once using the Database library.
26 | |
27 | */
28 |
29 | 'default' => 'mysql',
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Database Connections
34 | |--------------------------------------------------------------------------
35 | |
36 | | Here are each of the database connections setup for your application.
37 | | Of course, examples of configuring each database platform that is
38 | | supported by Laravel is shown below to make development simple.
39 | |
40 | |
41 | | All database work in Laravel is done through the PHP PDO facilities
42 | | so make sure you have the driver for your particular database of
43 | | choice installed on your machine before you begin development.
44 | |
45 | */
46 |
47 | 'connections' => array(
48 |
49 | 'sqlite' => array(
50 | 'driver' => 'sqlite',
51 | 'database' => __DIR__.'/../database/production.sqlite',
52 | 'prefix' => '',
53 | ),
54 |
55 | 'mysql' => array(
56 | 'driver' => 'mysql',
57 | 'host' => 'localhost',
58 | 'database' => 'database',
59 | 'username' => 'root',
60 | 'password' => '',
61 | 'charset' => 'utf8',
62 | 'collation' => 'utf8_unicode_ci',
63 | 'prefix' => '',
64 | ),
65 |
66 | 'pgsql' => array(
67 | 'driver' => 'pgsql',
68 | 'host' => 'localhost',
69 | 'database' => 'database',
70 | 'username' => 'root',
71 | 'password' => '',
72 | 'charset' => 'utf8',
73 | 'prefix' => '',
74 | 'schema' => 'public',
75 | ),
76 |
77 | 'sqlsrv' => array(
78 | 'driver' => 'sqlsrv',
79 | 'host' => 'localhost',
80 | 'database' => 'database',
81 | 'username' => 'root',
82 | 'password' => '',
83 | 'prefix' => '',
84 | ),
85 |
86 | ),
87 |
88 | /*
89 | |--------------------------------------------------------------------------
90 | | Migration Repository Table
91 | |--------------------------------------------------------------------------
92 | |
93 | | This table keeps track of all the migrations that have already run for
94 | | your application. Using this information, we can determine which of
95 | | the migrations on disk haven't actually been run in the database.
96 | |
97 | */
98 |
99 | 'migrations' => 'migrations',
100 |
101 | /*
102 | |--------------------------------------------------------------------------
103 | | Redis Databases
104 | |--------------------------------------------------------------------------
105 | |
106 | | Redis is an open source, fast, and advanced key-value store that also
107 | | provides a richer set of commands than a typical key-value systems
108 | | such as APC or Memcached. Laravel makes it easy to dig right in.
109 | |
110 | */
111 |
112 | 'redis' => array(
113 |
114 | 'cluster' => false,
115 |
116 | 'default' => array(
117 | 'host' => '127.0.0.1',
118 | 'port' => 6379,
119 | 'database' => 0,
120 | ),
121 |
122 | ),
123 |
124 | );
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/modess/git-pretty-stats)
2 |
3 | You know those cool graphs and statistics you can get for a repository on github? The things is that (unfortunately) not all git repositories are hosted on github for various reasons. This is the tool for rendering graphs for your repository that can be hosted anywhere, and it looks great.
4 |
5 | ## Features
6 |
7 | * Handles multiple repositories
8 | * Handles bare repositories
9 | * Statistics
10 | - Total commits
11 | - Total contributors
12 | - Average commits per contributor
13 | - First commit date
14 | - Latest commit date
15 | - Active for (X days)
16 | - Average commits per day
17 | * Graphs
18 | - Commits by date
19 | - Commits by hour of day
20 | - Commits by day of week
21 | - Commits by contributor
22 |
23 | ## Requirements
24 |
25 | * PHP >= 5.3.2
26 | * Git
27 |
28 | ## Installation
29 |
30 | Install dependencies using [Composer](http://getcomposer.org/)
31 |
32 | php composer.phar install
33 |
34 | You also need to set permission to the applications storage folder
35 |
36 | chmod -R 777 app/storage
37 |
38 | Clone the repositories you want to statistics and graphs for in to the **repositories** folder.
39 | ```php
40 | cd repositories
41 | git clone
42 | ```
43 | You can clone as many as you want to in to this folder.
44 |
45 | ### Web server configuration
46 |
47 | See [laravel-website-configs](https://github.com/daylerees/laravel-website-configs) by Dayle Rees for examples of Laravel web server configurations.
48 |
49 | * [Apache](https://github.com/daylerees/laravel-website-configs/blob/master/apache.conf)
50 | * [NginX](https://github.com/daylerees/laravel-website-configs/blob/master/nginx.conf)
51 |
52 | ### Configuration
53 | To manually override configuration, start by copying `app/config/git-pretty-stats.php` to `app/config/local/git-pretty-stats.php`. Then you can configure the following in that file:
54 |
55 | **Custom directory for repositories**
56 |
57 | Set the `repositoriesPath` to a relative path where you store your repositories
58 | ```php
59 | 'repositoriesPath' => '../../repositories'
60 | ```
61 | **Specify each repository path**
62 |
63 | Set the `repositoriesPath` to an array of paths. They can be either relative or absolute.
64 | ```php
65 | 'repositoriesPath' => array(
66 | '/var/www/web-project',
67 | '../test-project'
68 | );
69 | ```
70 |
71 | **E-mail aliases**
72 |
73 | Sometimes a user will contribute using different e-mail addresses to a repository. If you want to you can manually map the aliases for this. To add commits to `user_new@email.com` made by `user_old@email.com` you can do this (you can add as many as you like):
74 | ```php
75 | 'emailAliases' => array(
76 | 'user_old@email.com' => 'user_new@email.com'
77 | );
78 | ```
79 |
80 | ***
81 |
82 | Now go to your web browser and go to the URL where you've set everything up.
83 |
84 | *Disclaimer: the tool has been tested to work on repositories up to 10 000 commits. If your repository have a higher number of commits than that you might experience issues.*
85 |
86 | ## Upgrade from 0.3 to 0.4
87 |
88 | Since `0.4` the application is based on the Laravel framework and some changes have been made. All you need to do (except following the installation instructions again) is to move your `config.php` to `app/config/local/git-pretty-stats.php` if you have one.
89 |
90 | ## Contribute
91 |
92 | 1. Check for [open issues](https://github.com/modess/git-pretty-stats/issues) or open a fresh issue to start a discussion around a feature idea or a bug.
93 | 2. Fork the [git-pretty-stats](https://github.com/modess/git-pretty-stats) repository on Github to start making your changes.
94 | 3. Write test(s) which shows that the bug was fixed or that the feature works as expected.
95 | 5. Send a pull request. Make sure to add yourself to `CONTRIBUTORS.md`.
96 |
97 | If there is some certain statistics or graph that you are missing and would like to be added? [Create an issue](https://github.com/modess/git-pretty-stats/issues/new) and request it!
98 |
99 | ## Screenshots
100 | 
101 |
102 | 
103 |
104 | 
105 |
106 |
--------------------------------------------------------------------------------
/app/config/mail.php:
--------------------------------------------------------------------------------
1 | 'smtp',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | SMTP Host Address
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may provide the host address of the SMTP server used by your
26 | | applications. A default option is provided that is compatible with
27 | | the Postmark mail service, which will provide reliable delivery.
28 | |
29 | */
30 |
31 | 'host' => 'smtp.mailgun.org',
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | SMTP Host Port
36 | |--------------------------------------------------------------------------
37 | |
38 | | This is the SMTP port used by your application to delivery e-mails to
39 | | users of your application. Like the host we have set this value to
40 | | stay compatible with the Postmark e-mail application by default.
41 | |
42 | */
43 |
44 | 'port' => 587,
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Global "From" Address
49 | |--------------------------------------------------------------------------
50 | |
51 | | You may wish for all e-mails sent by your application to be sent from
52 | | the same address. Here, you may specify a name and address that is
53 | | used globally for all e-mails that are sent by your application.
54 | |
55 | */
56 |
57 | 'from' => array('address' => null, 'name' => null),
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | E-Mail Encryption Protocol
62 | |--------------------------------------------------------------------------
63 | |
64 | | Here you may specify the encryption protocol that should be used when
65 | | the application send e-mail messages. A sensible default using the
66 | | transport layer security protocol should provide great security.
67 | |
68 | */
69 |
70 | 'encryption' => 'tls',
71 |
72 | /*
73 | |--------------------------------------------------------------------------
74 | | SMTP Server Username
75 | |--------------------------------------------------------------------------
76 | |
77 | | If your SMTP server requires a username for authentication, you should
78 | | set it here. This will get used to authenticate with your server on
79 | | connection. You may also set the "password" value below this one.
80 | |
81 | */
82 |
83 | 'username' => null,
84 |
85 | /*
86 | |--------------------------------------------------------------------------
87 | | SMTP Server Password
88 | |--------------------------------------------------------------------------
89 | |
90 | | Here you may set the password required by your SMTP server to send out
91 | | messages from your application. This will be given to the server on
92 | | connection so that the application will be able to send messages.
93 | |
94 | */
95 |
96 | 'password' => null,
97 |
98 | /*
99 | |--------------------------------------------------------------------------
100 | | Sendmail System Path
101 | |--------------------------------------------------------------------------
102 | |
103 | | When using the "sendmail" driver to send e-mails, we will need to know
104 | | the path to where Sendmail lives on this server. A default path has
105 | | been provided here, which will work well on most of your systems.
106 | |
107 | */
108 |
109 | 'sendmail' => '/usr/sbin/sendmail -bs',
110 |
111 | /*
112 | |--------------------------------------------------------------------------
113 | | Mail "Pretend"
114 | |--------------------------------------------------------------------------
115 | |
116 | | When this option is enabled, e-mail will not actually be sent over the
117 | | web and will instead be written to your application's logs files so
118 | | you may inspect the message. This is great for local development.
119 | |
120 | */
121 |
122 | 'pretend' => false,
123 |
124 | );
125 |
--------------------------------------------------------------------------------
/app/lang/en/validation.php:
--------------------------------------------------------------------------------
1 | "The :attribute must be accepted.",
17 | "active_url" => "The :attribute is not a valid URL.",
18 | "after" => "The :attribute must be a date after :date.",
19 | "alpha" => "The :attribute may only contain letters.",
20 | "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
21 | "alpha_num" => "The :attribute may only contain letters and numbers.",
22 | "array" => "The :attribute must be an array.",
23 | "before" => "The :attribute must be a date before :date.",
24 | "between" => array(
25 | "numeric" => "The :attribute must be between :min and :max.",
26 | "file" => "The :attribute must be between :min and :max kilobytes.",
27 | "string" => "The :attribute must be between :min and :max characters.",
28 | "array" => "The :attribute must have between :min and :max items.",
29 | ),
30 | "confirmed" => "The :attribute confirmation does not match.",
31 | "date" => "The :attribute is not a valid date.",
32 | "date_format" => "The :attribute does not match the format :format.",
33 | "different" => "The :attribute and :other must be different.",
34 | "digits" => "The :attribute must be :digits digits.",
35 | "digits_between" => "The :attribute must be between :min and :max digits.",
36 | "email" => "The :attribute format is invalid.",
37 | "exists" => "The selected :attribute is invalid.",
38 | "image" => "The :attribute must be an image.",
39 | "in" => "The selected :attribute is invalid.",
40 | "integer" => "The :attribute must be an integer.",
41 | "ip" => "The :attribute must be a valid IP address.",
42 | "max" => array(
43 | "numeric" => "The :attribute may not be greater than :max.",
44 | "file" => "The :attribute may not be greater than :max kilobytes.",
45 | "string" => "The :attribute may not be greater than :max characters.",
46 | "array" => "The :attribute may not have more than :max items.",
47 | ),
48 | "mimes" => "The :attribute must be a file of type: :values.",
49 | "min" => array(
50 | "numeric" => "The :attribute must be at least :min.",
51 | "file" => "The :attribute must be at least :min kilobytes.",
52 | "string" => "The :attribute must be at least :min characters.",
53 | "array" => "The :attribute must have at least :min items.",
54 | ),
55 | "not_in" => "The selected :attribute is invalid.",
56 | "numeric" => "The :attribute must be a number.",
57 | "regex" => "The :attribute format is invalid.",
58 | "required" => "The :attribute field is required.",
59 | "required_if" => "The :attribute field is required when :other is :value.",
60 | "required_with" => "The :attribute field is required when :values is present.",
61 | "required_without" => "The :attribute field is required when :values is not present.",
62 | "same" => "The :attribute and :other must match.",
63 | "size" => array(
64 | "numeric" => "The :attribute must be :size.",
65 | "file" => "The :attribute must be :size kilobytes.",
66 | "string" => "The :attribute must be :size characters.",
67 | "array" => "The :attribute must contain :size items.",
68 | ),
69 | "unique" => "The :attribute has already been taken.",
70 | "url" => "The :attribute format is invalid.",
71 |
72 | /*
73 | |--------------------------------------------------------------------------
74 | | Custom Validation Language Lines
75 | |--------------------------------------------------------------------------
76 | |
77 | | Here you may specify custom validation messages for attributes using the
78 | | convention "attribute.rule" to name the lines. This makes it quick to
79 | | specify a specific custom language line for a given attribute rule.
80 | |
81 | */
82 |
83 | 'custom' => array(),
84 |
85 | /*
86 | |--------------------------------------------------------------------------
87 | | Custom Validation Attributes
88 | |--------------------------------------------------------------------------
89 | |
90 | | The following language lines are used to swap attribute place-holders
91 | | with something more reader friendly such as E-Mail Address instead
92 | | of "email". This simply helps us make messages a little cleaner.
93 | |
94 | */
95 |
96 | 'attributes' => array(),
97 |
98 | );
99 |
--------------------------------------------------------------------------------
/app/config/session.php:
--------------------------------------------------------------------------------
1 | 'file',
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Session Lifetime
24 | |--------------------------------------------------------------------------
25 | |
26 | | Here you may specify the number of minutes that you wish the session
27 | | to be allowed to remain idle before it expires. If you want them
28 | | to immediately expire on the browser closing, set that option.
29 | |
30 | */
31 |
32 | 'lifetime' => 120,
33 |
34 | 'expire_on_close' => false,
35 |
36 | /*
37 | |--------------------------------------------------------------------------
38 | | Session File Location
39 | |--------------------------------------------------------------------------
40 | |
41 | | When using the native session driver, we need a location where session
42 | | files may be stored. A default has been set for you but a different
43 | | location may be specified. This is only needed for file sessions.
44 | |
45 | */
46 |
47 | 'files' => storage_path().'/sessions',
48 |
49 | /*
50 | |--------------------------------------------------------------------------
51 | | Session Database Connection
52 | |--------------------------------------------------------------------------
53 | |
54 | | When using the "database" or "redis" session drivers, you may specify a
55 | | connection that should be used to manage these sessions. This should
56 | | correspond to a connection in your database configuration options.
57 | |
58 | */
59 |
60 | 'connection' => null,
61 |
62 | /*
63 | |--------------------------------------------------------------------------
64 | | Session Database Table
65 | |--------------------------------------------------------------------------
66 | |
67 | | When using the "database" session driver, you may specify the table we
68 | | should use to manage the sessions. Of course, a sensible default is
69 | | provided for you; however, you are free to change this as needed.
70 | |
71 | */
72 |
73 | 'table' => 'sessions',
74 |
75 | /*
76 | |--------------------------------------------------------------------------
77 | | Session Sweeping Lottery
78 | |--------------------------------------------------------------------------
79 | |
80 | | Some session drivers must manually sweep their storage location to get
81 | | rid of old sessions from storage. Here are the chances that it will
82 | | happen on a given request. By default, the odds are 2 out of 100.
83 | |
84 | */
85 |
86 | 'lottery' => array(2, 100),
87 |
88 | /*
89 | |--------------------------------------------------------------------------
90 | | Session Cookie Name
91 | |--------------------------------------------------------------------------
92 | |
93 | | Here you may change the name of the cookie used to identify a session
94 | | instance by ID. The name specified here will get used every time a
95 | | new session cookie is created by the framework for every driver.
96 | |
97 | */
98 |
99 | 'cookie' => 'laravel_session',
100 |
101 | /*
102 | |--------------------------------------------------------------------------
103 | | Session Cookie Path
104 | |--------------------------------------------------------------------------
105 | |
106 | | The session cookie path determines the path for which the cookie will
107 | | be regarded as available. Typically, this will be the root path of
108 | | your application but you are free to change this when necessary.
109 | |
110 | */
111 |
112 | 'path' => '/',
113 |
114 | /*
115 | |--------------------------------------------------------------------------
116 | | Session Cookie Domain
117 | |--------------------------------------------------------------------------
118 | |
119 | | Here you may change the domain of the cookie used to identify a session
120 | | in your application. This will determine which domains the cookie is
121 | | available to in your application. A sensible default has been set.
122 | |
123 | */
124 |
125 | 'domain' => null,
126 |
127 | /*
128 | |--------------------------------------------------------------------------
129 | | HTTPS Only Cookies
130 | |--------------------------------------------------------------------------
131 | |
132 | | By setting this option to true, session cookies will only be sent back
133 | | to the server if the browser has a HTTPS connection. This will keep
134 | | the cookie from being sent to you if it can not be done securely.
135 | |
136 | */
137 |
138 | 'secure' => false,
139 |
140 | );
141 |
--------------------------------------------------------------------------------
/app/GitPrettyStats/RepositoryFactory.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class RepositoryFactory
14 | {
15 | /**
16 | * Paths to repositories
17 | *
18 | * @var array
19 | */
20 | protected $paths;
21 |
22 | /**
23 | * Base directory for paths
24 | *
25 | * @var string
26 | */
27 | protected $baseDir;
28 |
29 | /**
30 | * File system handler
31 | *
32 | * @var Symfony\Component\Finder\Finder
33 | */
34 | protected $finder;
35 |
36 | /**
37 | * Loaded repositories
38 | *
39 | * @var array
40 | */
41 | protected $repositories;
42 |
43 | /**
44 | * Create a new factory
45 | *
46 | * @param Symfony\Component\Finder\Finder File system handler
47 | * @param string $baseDir Base directory for Git Pretty Stats
48 | * @return void
49 | */
50 | public function __construct(Finder $finder, $baseDir = null)
51 | {
52 | $this->finder = $finder;
53 | $this->baseDir = ($baseDir !== null) ? $baseDir : base_path();
54 | }
55 |
56 | /**
57 | * Get all paths to repositories from config
58 | *
59 | * @return array
60 | */
61 | public function getPaths ()
62 | {
63 | $paths = array();
64 |
65 | if (!$this->paths)
66 | {
67 | $repositoriesPath = Config::get('git-pretty-stats.repositoriesPath');
68 |
69 | // Repositories are specified as array in config
70 | if (is_array($repositoriesPath))
71 | {
72 | $paths = array();
73 | foreach ($repositoriesPath as $path) {
74 | $paths[] = $this->getFullPath($path);
75 | }
76 |
77 | $directories = $this->finder
78 | ->depth(0)
79 | ->directories()
80 | ->append($paths);
81 | }
82 | // Custom repository path
83 | elseif (!is_null($repositoriesPath))
84 | {
85 | $directories = $this->finder
86 | ->depth(0)
87 | ->directories()
88 | ->in($this->getFullPath($repositoriesPath));
89 | }
90 |
91 | // Real paths for all repositories
92 | foreach ($directories as $key => $directory) {
93 | $this->paths[$key] = $directory->getRealPath();
94 | }
95 | }
96 |
97 | return $this->paths;
98 | }
99 |
100 | /**
101 | * Get full path for directory
102 | *
103 | * @param string $path
104 | * @return string
105 | */
106 | public function getFullPath ($path)
107 | {
108 | return substr($path, 0, 1) == '/' ? $path : $this->baseDir . '/' . $path;
109 | }
110 |
111 | /**
112 | * Fetch all repositories
113 | *
114 | * @return array
115 | */
116 | public function all ($includeStatistics = false)
117 | {
118 | if ($this->repositories) {
119 | return $this->repositories;
120 | }
121 |
122 | // Load repositories
123 | $repositories = array();
124 | foreach ($this->getPaths() as $path) {
125 | if ($repository = $this->load($path, $includeStatistics)) {
126 | $repositories[ $repository->getName() ] = $repository;
127 | }
128 | }
129 |
130 | $this->repositories = $repositories;
131 |
132 | return $repositories;
133 | }
134 |
135 | /**
136 | * Get repository from short name (top level directory)
137 | *
138 | * @param string $name
139 | * @return GitPrettyStats\Repository|bool
140 | */
141 | public function fromName ($name)
142 | {
143 | return isset($this->repositories[$name]) ? $this->repositories[$name] : false;
144 | }
145 |
146 | /**
147 | * Load a repository for given path
148 | *
149 | * @codeCoverageIgnore
150 | * @param string $path
151 | * @return GitPrettyStats\Repository|false
152 | */
153 | public function load ($path, $includeStatistics = false)
154 | {
155 | if ( !is_dir($path)) {
156 | return false;
157 | }
158 |
159 | $cacheKey = sprintf('info:%s', $path);
160 |
161 | // Try cache
162 | $cached = Cache::get($cacheKey);
163 | if ($cached !== null) {
164 | return $cached;
165 | }
166 |
167 | // Load repository and store in cache
168 | $created = $this->create($path, $includeStatistics);
169 | Cache::put($cacheKey, $created, 60);
170 |
171 | return $created;
172 | }
173 |
174 | /**
175 | * Try to create repository from path
176 | *
177 | * @param str $path
178 | * @return false|Repository
179 | */
180 | public function create ($path, $includeStatistics = false)
181 | {
182 | try {
183 | $repository = new Repository($path);
184 |
185 | if ($includeStatistics === true) {
186 | $repository->addStatistics(array(
187 | new \Gitter\Statistics\Contributors,
188 | new \Gitter\Statistics\Date,
189 | new \Gitter\Statistics\Day,
190 | new \Gitter\Statistics\Hour
191 | ));
192 | }
193 |
194 | return $repository;
195 | } catch (\Exception $e) {
196 | return false;
197 | }
198 | }
199 |
200 | /**
201 | * Set repositories
202 | *
203 | * @param array $repositories
204 | */
205 | public function setRepositories (array $repositories)
206 | {
207 | $this->repositories = $repositories;
208 | }
209 |
210 | /**
211 | * Get repositories
212 | *
213 | * @return array
214 | */
215 | public function getRepositories ()
216 | {
217 | return $this->repositories;
218 | }
219 |
220 | /**
221 | * Return all repositories in array format
222 | *
223 | * @return array
224 | */
225 | public function toArray ($includeStatistics = false)
226 | {
227 | $toArray = array();
228 |
229 | foreach ($this->all($includeStatistics) as $repository) {
230 | $toArray[ $repository->getName() ] = array(
231 | 'name' => $repository->getName(),
232 | 'commits' => $repository->getTotalCommits(),
233 | 'branch' => $repository->getCurrentBranch()
234 | );
235 | }
236 |
237 | return $toArray;
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/app/config/app.php:
--------------------------------------------------------------------------------
1 | true,
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Application URL
21 | |--------------------------------------------------------------------------
22 | |
23 | | This URL is used by the console to properly generate URLs when using
24 | | the Artisan command line tool. You should set this to the root of
25 | | your application so that it is used when running Artisan tasks.
26 | |
27 | */
28 |
29 | 'url' => 'http://localhost',
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Application Timezone
34 | |--------------------------------------------------------------------------
35 | |
36 | | Here you may specify the default timezone for your application, which
37 | | will be used by the PHP date and date-time functions. We have gone
38 | | ahead and set this to a sensible default for you out of the box.
39 | |
40 | */
41 |
42 | 'timezone' => 'UTC',
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Application Locale Configuration
47 | |--------------------------------------------------------------------------
48 | |
49 | | The application locale determines the default locale that will be used
50 | | by the translation service provider. You are free to set this value
51 | | to any of the locales which will be supported by the application.
52 | |
53 | */
54 |
55 | 'locale' => 'en',
56 |
57 | /*
58 | |--------------------------------------------------------------------------
59 | | Encryption Key
60 | |--------------------------------------------------------------------------
61 | |
62 | | This key is used by the Illuminate encrypter service and should be set
63 | | to a random, 32 character string, otherwise these encrypted strings
64 | | will not be safe. Please do this before deploying an application!
65 | |
66 | */
67 |
68 | 'key' => 'YourSecretKey!!!',
69 |
70 | /*
71 | |--------------------------------------------------------------------------
72 | | Autoloaded Service Providers
73 | |--------------------------------------------------------------------------
74 | |
75 | | The service providers listed here will be automatically loaded on the
76 | | request to your application. Feel free to add your own services to
77 | | this array to grant expanded functionality to your applications.
78 | |
79 | */
80 |
81 | 'providers' => array(
82 |
83 | 'Illuminate\Foundation\Providers\ArtisanServiceProvider',
84 | 'Illuminate\Auth\AuthServiceProvider',
85 | 'Illuminate\Cache\CacheServiceProvider',
86 | 'Illuminate\Session\CommandsServiceProvider',
87 | 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider',
88 | 'Illuminate\Routing\ControllerServiceProvider',
89 | 'Illuminate\Cookie\CookieServiceProvider',
90 | 'Illuminate\Database\DatabaseServiceProvider',
91 | 'Illuminate\Encryption\EncryptionServiceProvider',
92 | 'Illuminate\Filesystem\FilesystemServiceProvider',
93 | 'Illuminate\Hashing\HashServiceProvider',
94 | 'Illuminate\Html\HtmlServiceProvider',
95 | 'Illuminate\Log\LogServiceProvider',
96 | 'Illuminate\Mail\MailServiceProvider',
97 | 'Illuminate\Database\MigrationServiceProvider',
98 | 'Illuminate\Pagination\PaginationServiceProvider',
99 | 'Illuminate\Queue\QueueServiceProvider',
100 | 'Illuminate\Redis\RedisServiceProvider',
101 | 'Illuminate\Remote\RemoteServiceProvider',
102 | 'Illuminate\Auth\Reminders\ReminderServiceProvider',
103 | 'Illuminate\Database\SeedServiceProvider',
104 | 'Illuminate\Session\SessionServiceProvider',
105 | 'Illuminate\Translation\TranslationServiceProvider',
106 | 'Illuminate\Validation\ValidationServiceProvider',
107 | 'Illuminate\View\ViewServiceProvider',
108 | 'Illuminate\Workbench\WorkbenchServiceProvider',
109 |
110 | 'Barryvdh\Cors\CorsServiceProvider',
111 | 'GitPrettyStats\Providers\RepositoryFactoryServiceProvider',
112 | ),
113 |
114 | /*
115 | |--------------------------------------------------------------------------
116 | | Service Provider Manifest
117 | |--------------------------------------------------------------------------
118 | |
119 | | The service provider manifest is used by Laravel to lazy load service
120 | | providers which are not needed for each request, as well to keep a
121 | | list of all of the services. Here, you may set its storage spot.
122 | |
123 | */
124 |
125 | 'manifest' => storage_path().'/meta',
126 |
127 | /*
128 | |--------------------------------------------------------------------------
129 | | Class Aliases
130 | |--------------------------------------------------------------------------
131 | |
132 | | This array of class aliases will be registered when this application
133 | | is started. However, feel free to register as many as you wish as
134 | | the aliases are "lazy" loaded so they don't hinder performance.
135 | |
136 | */
137 |
138 | 'aliases' => array(
139 |
140 | 'App' => 'Illuminate\Support\Facades\App',
141 | 'Artisan' => 'Illuminate\Support\Facades\Artisan',
142 | 'Auth' => 'Illuminate\Support\Facades\Auth',
143 | 'Blade' => 'Illuminate\Support\Facades\Blade',
144 | 'Cache' => 'Illuminate\Support\Facades\Cache',
145 | 'ClassLoader' => 'Illuminate\Support\ClassLoader',
146 | 'Config' => 'Illuminate\Support\Facades\Config',
147 | 'Controller' => 'Illuminate\Routing\Controller',
148 | 'Cookie' => 'Illuminate\Support\Facades\Cookie',
149 | 'Crypt' => 'Illuminate\Support\Facades\Crypt',
150 | 'DB' => 'Illuminate\Support\Facades\DB',
151 | 'Eloquent' => 'Illuminate\Database\Eloquent\Model',
152 | 'Event' => 'Illuminate\Support\Facades\Event',
153 | 'File' => 'Illuminate\Support\Facades\File',
154 | 'Form' => 'Illuminate\Support\Facades\Form',
155 | 'Hash' => 'Illuminate\Support\Facades\Hash',
156 | 'HTML' => 'Illuminate\Support\Facades\HTML',
157 | 'Input' => 'Illuminate\Support\Facades\Input',
158 | 'Lang' => 'Illuminate\Support\Facades\Lang',
159 | 'Log' => 'Illuminate\Support\Facades\Log',
160 | 'Mail' => 'Illuminate\Support\Facades\Mail',
161 | 'Paginator' => 'Illuminate\Support\Facades\Paginator',
162 | 'Password' => 'Illuminate\Support\Facades\Password',
163 | 'Queue' => 'Illuminate\Support\Facades\Queue',
164 | 'Redirect' => 'Illuminate\Support\Facades\Redirect',
165 | 'Redis' => 'Illuminate\Support\Facades\Redis',
166 | 'Request' => 'Illuminate\Support\Facades\Request',
167 | 'Response' => 'Illuminate\Support\Facades\Response',
168 | 'Route' => 'Illuminate\Support\Facades\Route',
169 | 'Schema' => 'Illuminate\Support\Facades\Schema',
170 | 'Seeder' => 'Illuminate\Database\Seeder',
171 | 'Session' => 'Illuminate\Support\Facades\Session',
172 | 'SSH' => 'Illuminate\Support\Facades\SSH',
173 | 'Str' => 'Illuminate\Support\Str',
174 | 'URL' => 'Illuminate\Support\Facades\URL',
175 | 'Validator' => 'Illuminate\Support\Facades\Validator',
176 | 'View' => 'Illuminate\Support\Facades\View',
177 |
178 | ),
179 |
180 | );
181 |
--------------------------------------------------------------------------------
/public/scripts/app.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('gitPrettyStats', [
6 | 'gitPrettyStats.templates',
7 | 'ui.router',
8 | 'snap',
9 | 'chieffancypants.loadingBar'
10 | ])
11 | .run(function($rootScope, $state, $stateParams, snapRemote) {
12 | $rootScope.$state = $state;
13 | $rootScope.$stateParams = $stateParams;
14 |
15 | $rootScope.$on('$locationChangeStart', function() {
16 | snapRemote.close();
17 | });
18 | })
19 | .config(function($stateProvider, $urlRouterProvider, snapRemoteProvider) {
20 | snapRemoteProvider.globalOptions.touchToDrag = false;
21 | $urlRouterProvider.otherwise("/repositories");
22 |
23 | $stateProvider
24 | .state('repositories', {
25 | url: '/repositories',
26 | templateUrl: 'main.html',
27 | controller: 'RepositoriesController',
28 | resolve: {
29 | repositories: function(Repository) {
30 | return Repository.all();
31 | }
32 | }
33 | })
34 | .state('repository', {
35 | parent: 'repositories',
36 | url: '/:name',
37 | templateUrl: 'repository.html',
38 | controller: 'RepositoryController',
39 | resolve: {
40 | repo: function($stateParams, Repository) {
41 | return Repository.get($stateParams.name);
42 | }
43 | }
44 | });
45 | });
46 |
47 | }());
48 |
49 | (function () {
50 | 'use strict';
51 |
52 | angular
53 | .module('gitPrettyStats')
54 | .factory('Repository', function($http) {
55 | var repository = {};
56 |
57 | repository.all = function() {
58 | return $http({
59 | method: 'GET',
60 | url: base_url + '/repository/'
61 | });
62 | };
63 |
64 | repository.get = function(name) {
65 | return $http({
66 | method: 'GET',
67 | url: base_url + '/repository/' + name
68 | });
69 | };
70 |
71 | return repository;
72 | });
73 |
74 | })();
75 |
76 | (function () {
77 | 'use strict';
78 |
79 | angular
80 | .module('gitPrettyStats')
81 | .directive('gpsStatistics', function() {
82 | return {
83 | restrict: 'E',
84 | templateUrl: 'statistics.html'
85 | };
86 | });
87 |
88 | })();
89 |
90 | (function () {
91 | 'use strict';
92 |
93 | angular
94 | .module('gitPrettyStats')
95 | .directive('gpsRepositoryList', function() {
96 | return {
97 | restrict: 'E',
98 | templateUrl: 'repository_list.html',
99 | scope: {
100 | 'repositories': '=',
101 | 'inline': '@'
102 | }
103 | };
104 | });
105 |
106 | })();
107 |
108 | (function () {
109 | 'use strict';
110 |
111 | angular
112 | .module('gitPrettyStats')
113 | .directive('gpsContributor', function($timeout) {
114 | return {
115 | restrict: 'E',
116 | replace: true,
117 | templateUrl: 'contributor.html',
118 | scope: {
119 | contributor: '='
120 | },
121 |
122 | link: function(scope, iElement) {
123 | $timeout(function() {
124 | $(iElement).find('.chart').highcharts({
125 | chart: {
126 | type: "areaspline",
127 | zoomType: "x"
128 | },
129 | title: {
130 | text: ""
131 | },
132 | plotOptions: {
133 | series: {
134 | lineWidth: 1,
135 | marker: {
136 | enabled: false
137 | }
138 | }
139 | },
140 | xAxis: {
141 | categories: scope.contributor.data.x,
142 | tickInterval: parseInt(scope.contributor.data.x.length / 10),
143 | labels: {
144 | rotation: -45,
145 | y: 35
146 | }
147 | },
148 | yAxis: {
149 | title: {
150 | text: ""
151 | }
152 | },
153 | series: [
154 | {
155 | name: "Commits",
156 | data: scope.contributor.data.y
157 | }
158 | ]
159 | });
160 | }, 50);
161 | }
162 | };
163 | });
164 |
165 | })();
166 |
167 | (function () {
168 | 'use strict';
169 |
170 | angular
171 | .module('gitPrettyStats')
172 | .directive('gpsCommitsByHourChart', function($timeout) {
173 | return {
174 | restrict: 'E',
175 | replace: true,
176 | template: '',
177 |
178 | link: function(scope) {
179 | var data = scope.charts.hour;
180 |
181 | $timeout(function() {
182 | $("#chart-commits-by-hour").highcharts({
183 | chart: {
184 | type: "bar"
185 | },
186 | title: {
187 | text: ""
188 | },
189 | xAxis: {
190 | categories: data.x
191 | },
192 | yAxis: {
193 | title: {
194 | text: ""
195 | }
196 | },
197 | series: [
198 | {
199 | name: "Commits",
200 | data: data.y
201 | }
202 | ]
203 | });
204 | }, 50);
205 | }
206 | };
207 | });
208 |
209 | })();
210 |
211 | (function () {
212 | 'use strict';
213 |
214 | angular
215 | .module('gitPrettyStats')
216 | .directive('gpsCommitsByDayChart', function($timeout) {
217 | return {
218 | restrict: 'E',
219 | replace: true,
220 | template: '',
221 |
222 | link: function(scope) {
223 | var data = scope.charts.day;
224 |
225 | $timeout(function() {
226 | $("#chart-commits-by-day").highcharts({
227 | chart: {
228 | type: "pie"
229 | },
230 | title: {
231 | text: ""
232 | },
233 | yAxis: {
234 | title: {
235 | text: ""
236 | }
237 | },
238 | series: [
239 | {
240 | name: "Commits",
241 | data: data
242 | }
243 | ]
244 | });
245 | }, 50);
246 | }
247 | };
248 | });
249 |
250 | })();
251 |
252 | (function () {
253 | 'use strict';
254 |
255 | angular
256 | .module('gitPrettyStats')
257 | .directive('gpsCommitsByDateChart', function($timeout) {
258 | return {
259 | restrict: 'E',
260 | replace: true,
261 | template: '',
262 |
263 | link: function(scope) {
264 | var data = scope.charts.date;
265 |
266 | $timeout(function() {
267 | $('#chart-commits-by-date').highcharts({
268 | chart: {
269 | type: 'areaspline',
270 | zoomType: 'x'
271 | },
272 | title: {
273 | text: ''
274 | },
275 | plotOptions: {
276 | series: {
277 | lineWidth: 1,
278 | marker: {
279 | enabled: false
280 | }
281 | }
282 | },
283 | xAxis: {
284 | categories: data.x,
285 | tickInterval: parseInt(data.x.length / 20),
286 | labels: {
287 | rotation: -45,
288 | y: 35
289 | }
290 | },
291 | yAxis: {
292 | title: {
293 | text: ''
294 | }
295 | },
296 | series: [
297 | {
298 | name: 'Commits',
299 | data: data.y
300 | }
301 | ]
302 | });
303 | }, 50);
304 | }
305 | };
306 | });
307 |
308 | })();
309 |
310 | (function () {
311 | 'use strict';
312 |
313 | angular
314 | .module('gitPrettyStats')
315 | .controller('RepositoryController', function($scope, repositories, repo) {
316 | $scope.repository = repo.data;
317 | $scope.repositories = repositories.data;
318 | $scope.charts = $scope.repository.data.charts;
319 | });
320 |
321 | }());
322 |
323 | (function () {
324 | 'use strict';
325 |
326 | angular
327 | .module('gitPrettyStats')
328 | .controller('RepositoriesController', function($scope, repositories) {
329 | $scope.repositories = repositories.data;
330 | });
331 |
332 | }());
333 |
--------------------------------------------------------------------------------
/app/tests/GitPrettyStats/RepositoryFactoryTest.php:
--------------------------------------------------------------------------------
1 | finder = m::mock('Symfony\Component\Finder\Finder');
18 |
19 | \Config::shouldReceive('get')
20 | ->zeroOrMoreTimes()
21 | ->with('git-pretty-stats.emailAliases')
22 | ->andReturn(array());
23 | }
24 |
25 | public function testGetPathsWithoutConfig ()
26 | {
27 | $firstRepository = m::mock('stdClass');
28 | $firstRepository->shouldReceive('getRealPath')->once()->andReturn('/absolute/path/repository');
29 |
30 | $secondRepository = m::mock('stdClass');
31 | $secondRepository->shouldReceive('getRealPath')->once()->andReturn('/absolute/path/other-repository');
32 |
33 | \Config::shouldReceive('get')
34 | ->once()
35 | ->with('git-pretty-stats.repositoriesPath')
36 | ->andReturn('repositories');
37 |
38 | $this->finder->shouldReceive('depth->directories->in')
39 | ->once()
40 | ->with('/var/www/git-pretty-stats/repositories')
41 | ->andReturn(array($firstRepository, $secondRepository));
42 |
43 | $factory = new RepositoryFactory($this->finder, '/var/www/git-pretty-stats');
44 |
45 | $this->assertEquals(
46 | array('/absolute/path/repository', '/absolute/path/other-repository'),
47 | $factory->getPaths(),
48 | 'Did not load repositories path without config'
49 | );
50 | }
51 |
52 | public function testGetPathsWithConfig ()
53 | {
54 | \Config::shouldReceive('get')
55 | ->once()
56 | ->with('git-pretty-stats.repositoriesPath')
57 | ->andReturn('non-default-dir');
58 |
59 | $firstRepository = m::mock('stdClass');
60 | $firstRepository->shouldReceive('getRealPath')->once()->andReturn('/absolute/path/repository');
61 |
62 | $secondRepository = m::mock('stdClass');
63 | $secondRepository->shouldReceive('getRealPath')->once()->andReturn('/absolute/path/other-repository');
64 |
65 | $this->finder->shouldReceive('depth->directories->in')
66 | ->once()
67 | ->with('/var/www/git-pretty-stats/non-default-dir')
68 | ->andReturn(array($firstRepository, $secondRepository));
69 |
70 | $factory = new RepositoryFactory($this->finder, '/var/www/git-pretty-stats');
71 |
72 | $this->assertEquals(
73 | array('/absolute/path/repository', '/absolute/path/other-repository'),
74 | $factory->getPaths(),
75 | 'Did not load repositories path with repository path set in config'
76 | );
77 | }
78 |
79 | public function testGetPathsWithConfigArray ()
80 | {
81 | $firstRepositoryPath = '/path/to/first-repo';
82 | $secondRepositoryPath = '/path/to/second-repo';
83 | $thirdRepositoryPath = 'relative/path';
84 |
85 | \Config::shouldReceive('get')
86 | ->once()
87 | ->with('git-pretty-stats.repositoriesPath')
88 | ->andReturn(array($firstRepositoryPath, $secondRepositoryPath, $thirdRepositoryPath));
89 |
90 | $firstRepository = m::mock('stdClass');
91 | $firstRepository->shouldReceive('getRealPath')->once()->andReturn($firstRepositoryPath);
92 |
93 | $secondRepository = m::mock('stdClass');
94 | $secondRepository->shouldReceive('getRealPath')->once()->andReturn($secondRepositoryPath);
95 |
96 | $thirdRepository = m::mock('stdClass');
97 | $thirdRepository->shouldReceive('getRealPath')->once()->andReturn($thirdRepositoryPath);
98 |
99 | $this->finder
100 | ->shouldReceive('depth->directories->append')
101 | ->once()
102 | ->andReturn(array($firstRepository, $secondRepository, $thirdRepository));
103 |
104 | $factory = new RepositoryFactory($this->finder, '/var/www/git-pretty-stats');
105 |
106 | $this->assertEquals(
107 | array($firstRepositoryPath, $secondRepositoryPath, $thirdRepositoryPath),
108 | $factory->getPaths(),
109 | 'Did not load repositories paths correctly with repository array set in config'
110 | );
111 | }
112 |
113 | public function testRepositoriesSetterAndGetter ()
114 | {
115 | $factory = new RepositoryFactory($this->finder);
116 |
117 | $repositories = array('first-repo', 'second-repo');
118 |
119 | $factory->setRepositories($repositories);
120 |
121 | $this->assertEquals(
122 | $repositories,
123 | $factory->getRepositories(),
124 | 'Repositories setter and getter failed'
125 | );
126 | }
127 |
128 | public function testAll ()
129 | {
130 | $firstRepository = m::mock('stdClass');
131 | $firstRepository->shouldReceive('getName')->once()->andReturn('first-repo');
132 |
133 | $secondRepository = m::mock('stdClass');
134 | $secondRepository->shouldReceive('getName')->once()->andReturn('second-repo');
135 |
136 | $factory = m::mock('GitPrettyStats\RepositoryFactory[getPaths,load]', array($this->finder));
137 | $factory
138 | ->shouldReceive('getPaths')
139 | ->once()
140 | ->andReturn(array('/first/path', '/second/path'))
141 | ->shouldReceive('load')
142 | ->once()
143 | ->with('/first/path', false)
144 | ->andReturn($firstRepository)
145 | ->shouldReceive('load')
146 | ->once()
147 | ->with('/second/path', false)
148 | ->andReturn($secondRepository);
149 |
150 | $this->assertEquals(
151 | array('first-repo' => $firstRepository, 'second-repo' => $secondRepository),
152 | $factory->all(),
153 | 'Did not return all repositories'
154 | );
155 | }
156 |
157 | public function testAllLazyLoad ()
158 | {
159 | $factory = new RepositoryFactory($this->finder);
160 |
161 | $repositories = array(
162 | 'first-repo' => '/path',
163 | 'second-repo' => '/other-path'
164 | );
165 |
166 | $factory->setRepositories($repositories);
167 |
168 | $this->assertEquals(
169 | $repositories,
170 | $factory->all(),
171 | 'Lazy load for repositories failed'
172 | );
173 | }
174 |
175 | public function testFromName ()
176 | {
177 | $factory = new RepositoryFactory($this->finder);
178 |
179 | $repositories = array(
180 | 'first-repo' => '/path',
181 | 'second-repo' => '/other-path'
182 | );
183 |
184 | $factory->setRepositories($repositories);
185 |
186 | $this->assertEquals(
187 | '/path',
188 | $factory->fromName('first-repo'),
189 | 'From name returned incorrect value'
190 | );
191 | }
192 |
193 | public function testGetFullPath ()
194 | {
195 | $factory = new RepositoryFactory($this->finder, '/var/www/dev');
196 |
197 | $paths = array(
198 | '/base-path' => '/base-path',
199 | 'relative-path' => '/var/www/dev/relative-path',
200 | './relative-repo' => '/var/www/dev/./relative-repo'
201 | );
202 |
203 | foreach ($paths as $path => $expected) {
204 | $this->assertEquals(
205 | $expected,
206 | $factory->getFullPath($path)
207 | );
208 | }
209 | }
210 |
211 | public function testToArray ()
212 | {
213 | $firstRepository = m::mock('stdClass');
214 | $firstRepository
215 | ->shouldReceive('getName')
216 | ->twice()
217 | ->andReturn('first-repo')
218 | ->shouldReceive('getCurrentBranch')
219 | ->once()
220 | ->andReturn('master')
221 | ->shouldReceive('getTotalCommits')
222 | ->once()
223 | ->andReturn(271);
224 |
225 | $secondRepository = m::mock('stdClass');
226 | $secondRepository
227 | ->shouldReceive('getName')
228 | ->twice()
229 | ->andReturn('second-repo')
230 | ->shouldReceive('getCurrentBranch')
231 | ->once()
232 | ->andReturn('master')
233 | ->shouldReceive('getTotalCommits')
234 | ->once()
235 | ->andReturn(173);
236 |
237 | $factory = m::mock('GitPrettyStats\RepositoryFactory[all]', array($this->finder));
238 | $factory->shouldReceive('all')->once()->andReturn(array($firstRepository, $secondRepository));
239 |
240 | $this->assertEquals(
241 | array(
242 | 'first-repo' => array(
243 | 'name' => 'first-repo',
244 | 'commits' => 271,
245 | 'branch' => 'master'
246 | ),
247 | 'second-repo' => array(
248 | 'name' => 'second-repo',
249 | 'commits' => 173,
250 | 'branch' => 'master'
251 | )
252 | ),
253 | $factory->toArray(),
254 | 'Did not return all repositories'
255 | );
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/app/assets/styles/_bootstrap_variables.scss:
--------------------------------------------------------------------------------
1 | $color-minor-amount: 10%;
2 | $color-major-amount: 25%;
3 |
4 | $color-red: #D86868;
5 | $color-red-light: lighten($color-red, $color-minor-amount);
6 | $color-red-lighter: lighten($color-red, $color-major-amount);
7 | $color-red-dark: darken($color-red, $color-minor-amount);
8 | $color-red-darker: darken($color-red, $color-major-amount);
9 |
10 | $color-cyan: #54c4a5;
11 | $color-cyan-light: lighten($color-cyan, $color-minor-amount);
12 | $color-cyan-lighter: lighten($color-cyan, $color-major-amount);
13 | $color-cyan-dark: darken($color-cyan, $color-minor-amount);
14 | $color-cyan-darker: darken($color-cyan, $color-major-amount);
15 |
16 | $color-blue: #46A4BB;
17 | $color-blue-light: lighten($color-blue, $color-minor-amount);
18 | $color-blue-lighter: lighten($color-blue, $color-major-amount);
19 | $color-blue-dark: darken($color-blue, $color-minor-amount);
20 | $color-blue-darker: darken($color-blue, $color-major-amount);
21 |
22 | //
23 | // Variables
24 | // --------------------------------------------------
25 |
26 |
27 | // Global values
28 | // --------------------------------------------------
29 |
30 | // Grays
31 | // -------------------------
32 |
33 | $gray-darker: lighten(#000, 13.5%) !default; // #222
34 | $gray-dark: lighten(#000, 20%) !default; // #333
35 | $gray: lighten(#000, 33.5%) !default; // #555
36 | $gray-light: lighten(#000, 60%) !default; // #999
37 | $gray-lighter: lighten(#000, 93.5%) !default; // #eee
38 |
39 | // Brand colors
40 | // -------------------------
41 |
42 | $brand-primary: #428bca !default;
43 | $brand-success: #5cb85c !default;
44 | $brand-warning: #f0ad4e !default;
45 | $brand-danger: #d9534f !default;
46 | $brand-info: $color-cyan-dark !default;
47 |
48 | // Scaffolding
49 | // -------------------------
50 |
51 | $body-bg: $color-cyan !default;
52 | $text-color: #fff !default;
53 |
54 | // Links
55 | // -------------------------
56 |
57 | $link-color: $brand-primary !default;
58 | $link-hover-color: darken($link-color, 15%) !default;
59 |
60 | // Typography
61 | // -------------------------
62 |
63 | $font-fallbacks-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default;
64 | $font-regular: "OpenSans", $font-fallbacks-serif;
65 | $font-bold: "OpenSans-bold", $font-fallbacks-serif;
66 | $font-italic: "OpenSans-italic", $font-fallbacks-serif;
67 | $font-light: "OpenSans-light", $font-fallbacks-serif;
68 |
69 | $font-family-sans-serif: $font-regular, "Helvetica Neue", Helvetica, Arial, sans-serif !default;
70 | $font-family-serif: Georgia, "Times New Roman", Times, serif !default;
71 | $font-family-monospace: Monaco, Menlo, Consolas, "Courier New", monospace !default;
72 | $font-family-base: $font-family-sans-serif !default;
73 |
74 | $font-size-base: 14px !default;
75 | $font-size-large: ceil($font-size-base * 1.25) !default; // ~18px
76 | $font-size-small: ceil($font-size-base * 0.85) !default; // ~12px
77 |
78 | $font-size-h1: floor($font-size-base * 2.6) !default; // ~36px
79 | $font-size-h2: floor($font-size-base * 2.15) !default; // ~30px
80 | $font-size-h3: ceil($font-size-base * 1.7) !default; // ~24px
81 | $font-size-h4: ceil($font-size-base * 1.25) !default; // ~18px
82 | $font-size-h5: $font-size-base !default;
83 | $font-size-h6: ceil($font-size-base * 0.85) !default; // ~12px
84 |
85 | $line-height-base: 1.428571429 !default; // 20/14
86 | $line-height-computed: floor($font-size-base * $line-height-base) !default; // ~20px
87 |
88 | $headings-font-family: $font-bold !default;
89 | $headings-font-weight: 500 !default;
90 | $headings-line-height: 1.1 !default;
91 | $headings-color: inherit !default;
92 |
93 |
94 | // Iconography
95 | // -------------------------
96 |
97 | $icon-font-path: "../fonts/" !default;
98 | $icon-font-name: "glyphicons-halflings-regular" !default;
99 |
100 |
101 | // Components
102 | // -------------------------
103 | // Based on 14px font-size and 1.428 line-height (~20px to start)
104 |
105 | $padding-base-vertical: 6px !default;
106 | $padding-base-horizontal: 12px !default;
107 |
108 | $padding-large-vertical: 10px !default;
109 | $padding-large-horizontal: 16px !default;
110 |
111 | $padding-small-vertical: 5px !default;
112 | $padding-small-horizontal: 10px !default;
113 |
114 | $line-height-large: 1.33 !default;
115 | $line-height-small: 1.5 !default;
116 |
117 | $border-radius-base: 0 !default;
118 | $border-radius-large: 0 !default;
119 | $border-radius-small: 0 !default;
120 |
121 | $component-active-color: #fff !default;
122 | $component-active-bg: $color-cyan-light !default;
123 |
124 | $caret-width-base: 4px !default;
125 | $caret-width-large: 5px !default;
126 |
127 | // Tables
128 | // -------------------------
129 |
130 | $table-cell-padding: 8px !default;
131 | $table-condensed-cell-padding: 5px !default;
132 |
133 | $table-bg: transparent !default; // overall background-color
134 | $table-bg-accent: #f9f9f9 !default; // for striping
135 | $table-bg-hover: #f5f5f5 !default;
136 | $table-bg-active: $table-bg-hover !default;
137 |
138 | $table-border-color: #ddd !default; // table and cell border
139 |
140 |
141 | // Buttons
142 | // -------------------------
143 |
144 | $btn-font-weight: normal !default;
145 |
146 | $btn-default-color: #333 !default;
147 | $btn-default-bg: #fff !default;
148 | $btn-default-border: #ccc !default;
149 |
150 | $btn-primary-color: #fff !default;
151 | $btn-primary-bg: $brand-primary !default;
152 | $btn-primary-border: $btn-primary-bg !default;
153 |
154 | $btn-success-color: #fff !default;
155 | $btn-success-bg: $brand-success !default;
156 | $btn-success-border: $btn-success-bg !default;
157 |
158 | $btn-warning-color: #fff !default;
159 | $btn-warning-bg: $brand-warning !default;
160 | $btn-warning-border: $btn-warning-bg !default;
161 |
162 | $btn-danger-color: #fff !default;
163 | $btn-danger-bg: $brand-danger !default;
164 | $btn-danger-border: $btn-danger-bg !default;
165 |
166 | $btn-info-color: #fff !default;
167 | $btn-info-bg: $brand-info !default;
168 | $btn-info-border: $btn-info-bg !default;
169 |
170 | $btn-link-disabled-color: $gray-light !default;
171 |
172 |
173 | // Forms
174 | // -------------------------
175 |
176 | $input-bg: #fff !default;
177 | $input-bg-disabled: $gray-lighter !default;
178 |
179 | $input-color: $gray !default;
180 | $input-border: #ccc !default;
181 | $input-border-radius: $border-radius-base !default;
182 | $input-border-focus: #66afe9 !default;
183 |
184 | $input-color-placeholder: $gray-light !default;
185 |
186 | $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
187 | $input-height-large: (floor($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
188 | $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
189 |
190 | $legend-color: $gray-dark !default;
191 | $legend-border-color: #e5e5e5 !default;
192 |
193 | $input-group-addon-bg: $gray-lighter !default;
194 | $input-group-addon-border-color: $input-border !default;
195 |
196 |
197 | // Dropdowns
198 | // -------------------------
199 |
200 | $dropdown-bg: $color-cyan-dark !default;
201 | $dropdown-border: $dropdown-bg !default;
202 | $dropdown-fallback-border: #ccc !default;
203 | $dropdown-divider-bg: #e5e5e5 !default;
204 |
205 | $dropdown-link-color: #fff !default;
206 | $dropdown-link-hover-color: darken($dropdown-link-color, 5%) !default;
207 | $dropdown-link-hover-bg: $color-cyan !default;
208 |
209 | $dropdown-link-active-color: $component-active-color !default;
210 | $dropdown-link-active-bg: $component-active-bg !default;
211 |
212 | $dropdown-link-disabled-color: $gray-light !default;
213 |
214 | $dropdown-header-color: $gray-light !default;
215 |
216 | $dropdown-caret-color: #000 !default;
217 |
218 |
219 | // COMPONENT VARIABLES
220 | // --------------------------------------------------
221 |
222 |
223 | // Z-index master list
224 | // -------------------------
225 | // Used for a bird's eye view of components dependent on the z-axis
226 | // Try to avoid customizing these :)
227 |
228 | $zindex-navbar: 1000 !default;
229 | $zindex-dropdown: 1000 !default;
230 | $zindex-popover: 1010 !default;
231 | $zindex-tooltip: 1030 !default;
232 | $zindex-navbar-fixed: 1030 !default;
233 | $zindex-modal-background: 1040 !default;
234 | $zindex-modal: 1050 !default;
235 |
236 | // Media queries breakpoints
237 | // --------------------------------------------------
238 |
239 | // Extra small screen / phone
240 | // Note: Deprecated $screen-xs and $screen-phone as of v3.0.1
241 | $screen-xs: 480px !default;
242 | $screen-xs-min: $screen-xs !default;
243 | $screen-phone: $screen-xs-min !default;
244 |
245 | // Small screen / tablet
246 | // Note: Deprecated $screen-sm and $screen-tablet as of v3.0.1
247 | $screen-sm: 768px !default;
248 | $screen-sm-min: $screen-sm !default;
249 | $screen-tablet: $screen-sm-min !default;
250 |
251 | // Medium screen / desktop
252 | // Note: Deprecated $screen-md and $screen-desktop as of v3.0.1
253 | $screen-md: 992px !default;
254 | $screen-md-min: $screen-md !default;
255 | $screen-desktop: $screen-md-min !default;
256 |
257 | // Large screen / wide desktop
258 | // Note: Deprecated $screen-lg and $screen-lg-desktop as of v3.0.1
259 | $screen-lg: 1200px !default;
260 | $screen-lg-min: $screen-lg !default;
261 | $screen-lg-desktop: $screen-lg-min !default;
262 |
263 | // So media queries don't overlap when required, provide a maximum
264 | $screen-xs-max: ($screen-sm-min - 1) !default;
265 | $screen-sm-max: ($screen-md-min - 1) !default;
266 | $screen-md-max: ($screen-lg-min - 1) !default;
267 |
268 |
269 | // Grid system
270 | // --------------------------------------------------
271 |
272 | // Number of columns in the grid system
273 | $grid-columns: 12 !default;
274 | // Padding, to be divided by two and applied to the left and right of all columns
275 | $grid-gutter-width: 30px !default;
276 | // Point at which the navbar stops collapsing
277 | $grid-float-breakpoint: $screen-sm-min !default;
278 |
279 |
280 | // Navbar
281 | // -------------------------
282 |
283 | // Basics of a navbar
284 | $navbar-height: 50px !default;
285 | $navbar-margin-bottom: $line-height-computed !default;
286 | $navbar-border-radius: $border-radius-base !default;
287 | $navbar-padding-horizontal: floor($grid-gutter-width / 2) !default;
288 | $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default;
289 |
290 | $navbar-default-color: #fff !default;
291 | $navbar-default-bg: $color-cyan-light !default;
292 | $navbar-default-border: $navbar-default-bg !default;
293 |
294 | // Navbar links
295 | $navbar-default-link-color: #fff !default;
296 | $navbar-default-link-hover-color: #333 !default;
297 | $navbar-default-link-hover-bg: transparent !default;
298 | $navbar-default-link-active-color: #555 !default;
299 | $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%) !default;
300 | $navbar-default-link-disabled-color: #ccc !default;
301 | $navbar-default-link-disabled-bg: transparent !default;
302 |
303 | // Navbar brand label
304 | $navbar-default-brand-color: $navbar-default-link-color !default;
305 | $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) !default;
306 | $navbar-default-brand-hover-bg: transparent !default;
307 |
308 | // Navbar toggle
309 | $navbar-default-toggle-hover-bg: #ddd !default;
310 | $navbar-default-toggle-icon-bar-bg: #ccc !default;
311 | $navbar-default-toggle-border-color: #ddd !default;
312 |
313 |
314 | // Inverted navbar
315 | //
316 | // Reset inverted navbar basics
317 | $navbar-inverse-color: $gray-light !default;
318 | $navbar-inverse-bg: #222 !default;
319 | $navbar-inverse-border: darken($navbar-inverse-bg, 10%) !default;
320 |
321 | // Inverted navbar links
322 | $navbar-inverse-link-color: $gray-light !default;
323 | $navbar-inverse-link-hover-color: #fff !default;
324 | $navbar-inverse-link-hover-bg: transparent !default;
325 | $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color !default;
326 | $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%) !default;
327 | $navbar-inverse-link-disabled-color: #444 !default;
328 | $navbar-inverse-link-disabled-bg: transparent !default;
329 |
330 | // Inverted navbar brand label
331 | $navbar-inverse-brand-color: $navbar-inverse-link-color !default;
332 | $navbar-inverse-brand-hover-color: #fff !default;
333 | $navbar-inverse-brand-hover-bg: transparent !default;
334 |
335 | // Inverted navbar toggle
336 | $navbar-inverse-toggle-hover-bg: #333 !default;
337 | $navbar-inverse-toggle-icon-bar-bg: #fff !default;
338 | $navbar-inverse-toggle-border-color: #333 !default;
339 |
340 |
341 | // Navs
342 | // -------------------------
343 |
344 | $nav-link-padding: 10px 15px !default;
345 | $nav-link-hover-bg: $gray-lighter !default;
346 |
347 | $nav-disabled-link-color: $gray-light !default;
348 | $nav-disabled-link-hover-color: $gray-light !default;
349 |
350 | $nav-open-link-hover-color: #fff !default;
351 | $nav-open-caret-border-color: #fff !default;
352 |
353 | // Tabs
354 | $nav-tabs-border-color: $color-cyan-dark !default;
355 |
356 | $nav-tabs-link-hover-border-color: $color-cyan-dark !default;
357 |
358 | $nav-tabs-active-link-hover-bg: $color-cyan-dark !default;
359 | $nav-tabs-active-link-hover-color: #fff !default;
360 | $nav-tabs-active-link-hover-border-color: $color-cyan-dark !default;
361 |
362 | $nav-tabs-justified-link-border-color: #ddd !default;
363 | $nav-tabs-justified-active-link-border-color: $body-bg !default;
364 |
365 | // Pills
366 | $nav-pills-border-radius: $border-radius-base !default;
367 | $nav-pills-active-link-hover-bg: $component-active-bg !default;
368 | $nav-pills-active-link-hover-color: $component-active-color !default;
369 |
370 |
371 | // Pagination
372 | // -------------------------
373 |
374 | $pagination-bg: #fff !default;
375 | $pagination-border: #ddd !default;
376 |
377 | $pagination-hover-bg: $gray-lighter !default;
378 |
379 | $pagination-active-bg: $brand-primary !default;
380 | $pagination-active-color: #fff !default;
381 |
382 | $pagination-disabled-color: $gray-light !default;
383 |
384 |
385 | // Pager
386 | // -------------------------
387 |
388 | $pager-border-radius: 15px !default;
389 | $pager-disabled-color: $gray-light !default;
390 |
391 |
392 | // Jumbotron
393 | // -------------------------
394 |
395 | $jumbotron-padding: 30px !default;
396 | $jumbotron-color: inherit !default;
397 | $jumbotron-bg: $gray-lighter !default;
398 | $jumbotron-heading-color: inherit !default;
399 | $jumbotron-font-size: ceil($font-size-base * 1.5) !default;
400 |
401 |
402 | // Form states and alerts
403 | // -------------------------
404 |
405 | $state-success-text: #468847 !default;
406 | $state-success-bg: #dff0d8 !default;
407 | $state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default;
408 |
409 | $state-info-text: #3a87ad !default;
410 | $state-info-bg: #d9edf7 !default;
411 | $state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default;
412 |
413 | $state-warning-text: #c09853 !default;
414 | $state-warning-bg: #fcf8e3 !default;
415 | $state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default;
416 |
417 | $state-danger-text: #b94a48 !default;
418 | $state-danger-bg: #f2dede !default;
419 | $state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default;
420 |
421 |
422 | // Tooltips
423 | // -------------------------
424 | $tooltip-max-width: 200px !default;
425 | $tooltip-color: #fff !default;
426 | $tooltip-bg: #000 !default;
427 |
428 | $tooltip-arrow-width: 5px !default;
429 | $tooltip-arrow-color: $tooltip-bg !default;
430 |
431 |
432 | // Popovers
433 | // -------------------------
434 | $popover-bg: #fff !default;
435 | $popover-max-width: 276px !default;
436 | $popover-border-color: rgba(0,0,0,.2) !default;
437 | $popover-fallback-border-color: #ccc !default;
438 |
439 | $popover-title-bg: darken($popover-bg, 3%) !default;
440 |
441 | $popover-arrow-width: 10px !default;
442 | $popover-arrow-color: #fff !default;
443 |
444 | $popover-arrow-outer-width: ($popover-arrow-width + 1) !default;
445 | $popover-arrow-outer-color: rgba(0,0,0,.25) !default;
446 | $popover-arrow-outer-fallback-color: #999 !default;
447 |
448 |
449 | // Labels
450 | // -------------------------
451 |
452 | $label-default-bg: $gray-light !default;
453 | $label-primary-bg: $brand-primary !default;
454 | $label-success-bg: $brand-success !default;
455 | $label-info-bg: $brand-info !default;
456 | $label-warning-bg: $brand-warning !default;
457 | $label-danger-bg: $brand-danger !default;
458 |
459 | $label-color: #fff !default;
460 | $label-link-hover-color: #fff !default;
461 |
462 |
463 | // Modals
464 | // -------------------------
465 | $modal-inner-padding: 20px !default;
466 |
467 | $modal-title-padding: 15px !default;
468 | $modal-title-line-height: $line-height-base !default;
469 |
470 | $modal-content-bg: #fff !default;
471 | $modal-content-border-color: rgba(0,0,0,.2) !default;
472 | $modal-content-fallback-border-color: #999 !default;
473 |
474 | $modal-backdrop-bg: #000 !default;
475 | $modal-header-border-color: #e5e5e5 !default;
476 | $modal-footer-border-color: $modal-header-border-color !default;
477 |
478 |
479 | // Alerts
480 | // -------------------------
481 | $alert-padding: 15px !default;
482 | $alert-border-radius: $border-radius-base !default;
483 | $alert-link-font-weight: bold !default;
484 |
485 | $alert-success-bg: $state-success-bg !default;
486 | $alert-success-text: $state-success-text !default;
487 | $alert-success-border: $state-success-border !default;
488 |
489 | $alert-info-bg: $state-info-bg !default;
490 | $alert-info-text: $state-info-text !default;
491 | $alert-info-border: $state-info-border !default;
492 |
493 | $alert-warning-bg: $state-warning-bg !default;
494 | $alert-warning-text: $state-warning-text !default;
495 | $alert-warning-border: $state-warning-border !default;
496 |
497 | $alert-danger-bg: $state-danger-bg !default;
498 | $alert-danger-text: $state-danger-text !default;
499 | $alert-danger-border: $state-danger-border !default;
500 |
501 |
502 | // Progress bars
503 | // -------------------------
504 | $progress-bg: #f5f5f5 !default;
505 | $progress-bar-color: #fff !default;
506 |
507 | $progress-bar-bg: $brand-primary !default;
508 | $progress-bar-success-bg: $brand-success !default;
509 | $progress-bar-warning-bg: $brand-warning !default;
510 | $progress-bar-danger-bg: $brand-danger !default;
511 | $progress-bar-info-bg: $brand-info !default;
512 |
513 |
514 | // List group
515 | // -------------------------
516 | $list-group-bg: $color-cyan-dark !default;
517 | $list-group-border: $color-cyan !default;
518 | $list-group-border-radius: $border-radius-base !default;
519 |
520 | $list-group-hover-bg: $color-cyan-light !default;
521 | $list-group-active-color: $color-cyan-darker !default;
522 | $list-group-active-bg: $component-active-bg !default;
523 | $list-group-active-border: $list-group-active-bg !default;
524 |
525 | $list-group-link-color: #fff !default;
526 | $list-group-link-heading-color: #333 !default;
527 |
528 |
529 | // Panels
530 | // -------------------------
531 | $panel-bg: #fff !default;
532 | $panel-inner-border: #ddd !default;
533 | $panel-border-radius: $border-radius-base !default;
534 | $panel-footer-bg: #f5f5f5 !default;
535 |
536 | $panel-default-text: $gray-dark !default;
537 | $panel-default-border: #ddd !default;
538 | $panel-default-heading-bg: #f5f5f5 !default;
539 |
540 | $panel-primary-text: #fff !default;
541 | $panel-primary-border: $brand-primary !default;
542 | $panel-primary-heading-bg: $brand-primary !default;
543 |
544 | $panel-success-text: $state-success-text !default;
545 | $panel-success-border: $state-success-border !default;
546 | $panel-success-heading-bg: $state-success-bg !default;
547 |
548 | $panel-warning-text: $state-warning-text !default;
549 | $panel-warning-border: $state-warning-border !default;
550 | $panel-warning-heading-bg: $state-warning-bg !default;
551 |
552 | $panel-danger-text: $state-danger-text !default;
553 | $panel-danger-border: $state-danger-border !default;
554 | $panel-danger-heading-bg: $state-danger-bg !default;
555 |
556 | $panel-info-text: $state-info-text !default;
557 | $panel-info-border: $state-info-border !default;
558 | $panel-info-heading-bg: $state-info-bg !default;
559 |
560 |
561 | // Thumbnails
562 | // -------------------------
563 | $thumbnail-padding: 4px !default;
564 | $thumbnail-bg: $body-bg !default;
565 | $thumbnail-border: #ddd !default;
566 | $thumbnail-border-radius: $border-radius-base !default;
567 |
568 | $thumbnail-caption-color: $text-color !default;
569 | $thumbnail-caption-padding: 9px !default;
570 |
571 |
572 | // Wells
573 | // -------------------------
574 | $well-bg: #f5f5f5 !default;
575 |
576 |
577 | // Badges
578 | // -------------------------
579 | $badge-color: #fff !default;
580 | $badge-link-hover-color: #fff !default;
581 | $badge-bg: $gray-light !default;
582 |
583 | $badge-active-color: $link-color !default;
584 | $badge-active-bg: #fff !default;
585 |
586 | $badge-font-weight: bold !default;
587 | $badge-line-height: 1 !default;
588 | $badge-border-radius: 10px !default;
589 |
590 |
591 | // Breadcrumbs
592 | // -------------------------
593 | $breadcrumb-bg: #f5f5f5 !default;
594 | $breadcrumb-color: #ccc !default;
595 | $breadcrumb-active-color: $gray-light !default;
596 | $breadcrumb-separator: "/" !default;
597 |
598 |
599 | // Carousel
600 | // ------------------------
601 |
602 | $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6) !default;
603 |
604 | $carousel-control-color: #fff !default;
605 | $carousel-control-width: 15% !default;
606 | $carousel-control-opacity: .5 !default;
607 | $carousel-control-font-size: 20px !default;
608 |
609 | $carousel-indicator-active-bg: #fff !default;
610 | $carousel-indicator-border-color: #fff !default;
611 |
612 | $carousel-caption-color: #fff !default;
613 |
614 |
615 | // Close
616 | // ------------------------
617 | $close-font-weight: bold !default;
618 | $close-color: #000 !default;
619 | $close-text-shadow: 0 1px 0 #fff !default;
620 |
621 |
622 | // Code
623 | // ------------------------
624 | $code-color: #c7254e !default;
625 | $code-bg: #f9f2f4 !default;
626 |
627 | $pre-bg: #f5f5f5 !default;
628 | $pre-color: $gray-dark !default;
629 | $pre-border-color: #ccc !default;
630 | $pre-scrollable-max-height: 340px !default;
631 |
632 | // Type
633 | // ------------------------
634 | $text-muted: $gray-light !default;
635 | $abbr-border-color: $gray-light !default;
636 | $headings-small-color: $gray-light !default;
637 | $blockquote-small-color: $gray-light !default;
638 | $blockquote-border-color: $gray-lighter !default;
639 | $page-header-border-color: $gray-lighter !default;
640 |
641 | // Miscellaneous
642 | // -------------------------
643 |
644 | // Hr border color
645 | $hr-border: $color-cyan-light !default;
646 |
647 | // Horizontal forms & lists
648 | $component-offset-horizontal: 180px !default;
649 |
650 |
651 | // Container sizes
652 | // --------------------------------------------------
653 |
654 | // Small screen / tablet
655 | $container-tablet: ((720px + $grid-gutter-width)) !default;
656 | $container-sm: $container-tablet !default;
657 |
658 | // Medium screen / desktop
659 | $container-desktop: ((940px + $grid-gutter-width)) !default;
660 | $container-md: $container-desktop !default;
661 |
662 | // Large screen / wide desktop
663 | $container-large-desktop: ((1140px + $grid-gutter-width)) !default;
664 | $container-lg: $container-large-desktop !default;
665 |
--------------------------------------------------------------------------------