.gitignore000066400000000000000000000006311516067322700130570ustar00rootroot00000000000000cache/ vendor/ build/ *.diff *.err *.orig *.log *.rej *.swo *.swp *.zip *.vi *~ *.sass-cache .DS_Store ._* Thumbs.db .cache node_modules /config.ini /git-test /git-test2 /bower_components .idea/workspace.xml .idea/tasks.xml .idea/profiles_settings.xml .idea/inspectionProfiles/Project_Default.xml .idea/inspectionProfiles/profiles_settings.xml node_modules/.yarn-integrity /public/prod /src/twig/layout.twig.idea/000077500000000000000000000000001516067322700120475ustar00rootroot00000000000000.idea/codeStyleSettings.xml000066400000000000000000000004251516067322700162460ustar00rootroot00000000000000 .idea/codeStyles/000077500000000000000000000000001516067322700141655ustar00rootroot00000000000000.idea/codeStyles/codeStyleConfig.xml000066400000000000000000000002251516067322700177670ustar00rootroot00000000000000 .idea/gitlist.iml000066400000000000000000000116731516067322700142410ustar00rootroot00000000000000 .idea/misc.xml000066400000000000000000000002561516067322700135270ustar00rootroot00000000000000 .idea/modules.xml000066400000000000000000000004121516067322700142360ustar00rootroot00000000000000 .idea/php.xml000066400000000000000000000074521516067322700133700ustar00rootroot00000000000000 .idea/vcs.xml000066400000000000000000000002471516067322700133670ustar00rootroot00000000000000 .npmrc000066400000000000000000000001051516067322700122030ustar00rootroot00000000000000https://registry.npmjs.org/= registry=https://registry.npmjs.org/ .travis.yml000077500000000000000000000017601516067322700132070ustar00rootroot00000000000000sudo: required dist: trusty language: php php: - '7.1' - '7.2' - nightly before_install: - composer config --global github-oauth.github.com $GITHUB_TOKEN before_script: composer install notifications: email: recipients: - alabard@gmail.com on_success: change on_failure: change env: global: secure: NPmPNUmMuxBj9xHcXLFUf3KxPUMlXgcuZeVM3IaV4/lCBd4GXQ5wVTKBb7N7v90EDijLvNXsIQ0sSDnVAkteTi+O0sJUcAG1DKJMKIDU2HYd2llM+hbo2Nhfpm/jTOuZg6VTqFLKK1wUVFXPtHyX6sbbJRDCstDdLIb10noDnk5iaTP2LtDKd2lMcCjsymP1d+sqCwwREwKj/TIGqoFdkUTKbxy2MgQrV8kT+WwXAhq5l2x7MaQMOO2zcN+qI3T9yYyLyuB0DYnxv+U1AByQQoSG37rEySy6orZNDuN9BOMNFak2knbO0RUWuLnR3/Vj4VLDZx617ONNJ29PUgXRuzeDg51KBMo6BMT+PKUJdBx6710hOx3clgr3CRHJ/HzTcU4JiY6LRvxUsegLd0J2oGU7KBrVBbswSi5xY/NIhDp8dpRlKbmslDrg4kJgd5bbuyNOWsM5irGyYIi8R2oYzNJBQAKAQEjyjI2T/m0u+g8gGAlTr4fjWRkCBgz0ufnJu1tdnXeJ5K6XxP+NqvpXMHv+kehL4ElWAm3CHSLyeQBruzX2qfL3Ik4vAI5/RWeqoJGcPaB2vxELulcexqLkegXg+tOnXedKEvrVaT1l5SejdbPyO57PKL3t/H8ru3dpInS4muqD3EcZ/2eZB1puPEE/+bJWmNMqHAYfOW2mMwc= Gruntfile.js000077500000000000000000000040201516067322700133630ustar00rootroot00000000000000const prodDir = require('./package').corifeus["prod-dir"] module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-less'); const builder = require(`corifeus-builder`); const loader = new builder.loader(grunt); loader.js({ replacer: { type: 'p3x', npmio: false, }, config: { clean: { generated: [ `public/${prodDir}/css`, `public/${prodDir}/twemoji`, `public/${prodDir}/webpack`, ], css: [ ] /* themes: [ themeDir ], fonts: [ 'public/fonts' ] */ }, copy: { tweomji: { files: [ { cwd: 'node_modules/twemoji/2/svg', expand: true, src: [ '**', ], dest: `./public/${prodDir}/twemoji/svg` }, ] }, }, less: { development: require('./src/browser/grunt/less').lessSettings(grunt), }, watch: { less: { files: ['src/browser/less/*.*'], tasks: ['clean:css', 'less'], options: { atBegin: true, //spawn: false, }, }, } } }); grunt.registerTask('default', ['clean', 'less', 'copy', 'cory-npm', 'cory-replace']); grunt.registerTask('build', ['default']); }; INSTALL.md000066400000000000000000000077331516067322700125310ustar00rootroot00000000000000[//]: #@corifeus-header # 🤖 P3X Gitlist - An enhanced elegant, feature rich and modern git ui repository viewer [//]: #@corifeus-header:end # GitList Installation * Download GitList from [https://github.com/patrikx3/gitlist/releases](https://github.com/patrikx3/gitlist/releases/) and decompress to your `/var/www/gitlist` folder, or anywhere else you want to place GitList. * Rename the `config.example.ini-example` file to `config.ini`. * Open up the `config.ini` and configure your installation. You'll have to provide where your repositories are located and the base GitList URL (in our case, http://localhost/gitlist). * Create the cache folder and give read/write permissions to your web server user: ``` cd /var/www/gitlist mkdir -p cache chmod 777 cache ``` That's it, installation complete! ## Webserver configuration Apache is the "default" webserver for GitList. You will find the configuration inside the `.htaccess` file. However, nginx and lighttpd are also supported. To make it to be more secure: All `PHP` files will be in the `root` and only `index.php`, `images`, `icons`, `svg`, `css`, `js`bundle files will be in the `public` subdir. ### nginx server.conf ``` server { server_name MYSERVER; access_log /var/log/nginx/MYSERVER.access.log combined; error_log /var/log/nginx/MYSERVER.error.log error; root /var/www/DIR/public; index index.php; # auth_basic "Restricted"; # auth_basic_user_file .htpasswd; location = /robots.txt { allow all; log_not_found off; access_log off; } location ~* ^/index.php.*$ { fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # if you're using php7.2-fpm via socket fastcgi_pass unix:/var/run/php7.2-fpm.sock; include snippets/fastcgi-php.conf;; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass $php_listener; } location ~ /\.ht { deny all; } location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { add_header Vary "Accept-Encoding"; expires max; try_files $uri @gitlist; tcp_nodelay off; tcp_nopush on; } # location ~* \.(git|svn|patch|htaccess|log|route|plist|inc|json|pl|po|sh|ini|sample|kdev4)$ { # deny all; # } } ``` ### lighthttpd I do not use `lighthttpd`, but you know what I mean. Make sure only, the `gitlist/public` folder should be enabled. ``` # GitList is located in /var/www/gitlist/ server.document-root = "/var/www" url.rewrite-once = ( "^/gitlist/web/.+" => "$0", "^/gitlist/favicon\.ico$" => "$0", "^/gitlist(/[^\?]*)(\?.*)?" => "/gitlist/index.php$1$2" ) ``` ### hiawatha I do not use `hiawatha`, but you know what I mean. Make sure only, the `gitlist/public` folder should be enabled. ``` UrlToolkit { ToolkitID = gitlist RequestURI isfile Return # If you have example.com/gitlist/ ; Otherwise remove "/gitlist" below Match ^/gitlist/.* Rewrite /gitlist/index.php Match ^/gitlist/.*\.ini DenyAccess } ``` [//]: #@corifeus-footer --- [**P3X-GITLIST**](https://pages.corifeus.com/gitlist) Build v2018.12.13-4 [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) ## P3X Sponsors [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![NoSQLBooster](https://cdn.corifeus.com/assets/png/nosqlbooster-70x70.png)](https://www.nosqlbooster.com/) [The Smartest IDE for MongoDB](https://www.nosqlbooster.com) [//]: #@corifeus-footer:endLICENSE000066400000000000000000000051721516067322700121010ustar00rootroot00000000000000MIT License Copyright (c) 2018 Patrik Laszlo / patrikx3 / https://patrikx3.com and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ Copyright (c) 2012-2015, Klaus Silveira and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GitList nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. README.md000066400000000000000000000145351516067322700123560ustar00rootroot00000000000000[//]: #@corifeus-header [![Build Status](https://travis-ci.org/patrikx3/gitlist.svg?branch=master)](https://travis-ci.org/patrikx3/gitlist) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m780749701-41bcade28c1ea8154eda7cca.svg)](https://uptimerobot.patrikx3.com/) # 🤖 P3X Gitlist - An enhanced elegant, feature rich and modern git ui repository viewer v2018.12.13-4 This is an open-source project. Star this repository, if you like it, or even donate! Thank you so much! :) I run my own server with dynamic IP address, so, it may happen, that the server can not be reachable for about max 15 minutes, due to nature of the dynamic DNS. The server may also be unreachable, when I backup the SSD with Clonzilla (very rarely) or an electrical issue (but this should not happen again). When the server is down, please hang on for 15-30 minutes and the server will be back up. All my domains (patrikx3.com and corifeus.com) could have errors, since I am developing in my free time. However, it is usually stable. **Bugs are evident™ - MATRIX️** ### Node Version Requirement ``` >=10.13.0 ``` ### Built on Node ``` v11.4.0 ``` The ```async``` and ```await``` keywords are required. Install NodeJs: https://nodejs.org/en/download/package-manager/ # Description [//]: #@corifeus-header:end This is a new GitList with web workers, multiple themes (dark/light), sub-modules, uglify-es, webpack, toast, pure Bootstrap 3 and upgraded to PHP7.1 with all components. Usually, even a small router can use it with 64MB `PHP memory limit`. #### Warning For the actual released `zip` file does not need `NodeJS` or anything like that. Only a `web server`, `PHP 7.1` and `GIT`. See releases: https://github.com/patrikx3/gitlist/releases ### PHP > 7.1 only [README](artifacts/php-7.2-ubuntu.md) # Release version/update info ## Package Done, just put on your server, nothing to build: https://github.com/patrikx3/gitlist/releases ### Web server You might need to tune your web server, to only parse the `public/index.php` PHP script, so you can view your `php` files in `P3X GitList`. ## There is a changing break ### First Before, everything was in the `root` of the web server. Which is not secure. For now, you can create a folder eg. `/var/www/gitlist.me.com/` and make sure, that you server does not point to `/var/www/gitlist.me.com/`, but instead, point to `/var/www/gitlist.me.com/public`. ### Second The `config.ini` file with `url_subdir` or later `clone_subdir` variable has been changed to the `git_clone_subdir` variable. ### Third 😀 I removed `Babel`, we started to upgrade in 2018 on this repo. If you want use an older `Browser` (like iPhone 5), you can probably install latest `Chrome` and it will work. Besides, without `Babel` the `JavaScript` is much faster. (At work, without `Babel`, smaller `JS` bundle files and works about `20x` faster.) ### Fourth I have disabled loading everything in `twig`, besides the `diffs` are loading via `AJAX` and `web workers`, I made it to work huge commits with `64MB` `PHP`. See in action: https://gitlist.patrikx3.com/gitlist.git/commit/f1e4d5b938c8f1a6cd178aeea2e9e86111ea5323#p3x-gitlist-diff-93 ### Fifth If you upload a bigger binary file, it is important, that your web server allows to upload bigger files, because I found an error with `NGINX` as: ```text Request Entity Too Large ``` I resolved in the `NGINX` web server configuration file `nginx.conf` as: `client_max_body_size 64M;` Reference: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size So, this is only for testing. You should limit for some max size, that you want it at maximum, really. ## The last version with Babel https://github.com/patrikx3/gitlist/releases/tag/2.0.4-579 The following versions are not using `Babel`!!!! Yikes! # TODO and Change log [Change log](changelog.md) ... [TODO](todo.md) ... # Old info https://github.com/klaussilveira/gitlist ### Last merge from `klaussilveira` #### Gitlist https://github.com/klaussilveira/gitlist/commits/master Nov 14, 2018 #### Gitter https://github.com/klaussilveira/gitter/commits/master Jun 1, 2018 # Live demo http://gitlist.patrikx3.com/ # Installation ## Requirements By now `composer` is not enough. We are using `webpack`, `less`, `grunt` ... For the build on your workstation (less, Bootstrap themes, and webpack): * ```NodeJs``` >= 10 * ```Grunt``` (npm install -g npm grunt-cli) * `Composer` In order to run GitList on your server, you'll need: * ```git``` * ```Apache``` with ```mod_rewrite``` enabled or ```nginx``` * ```PHP``` >= 7.1 ## By hand If you have Composer in your path, things get easier. But you know the drill. If want to get the project dependencies, and build everything: ``` git clone https://github.com/patrikx3/gitlist.git curl -s http://getcomposer.org/installer | php php composer.phar install # i use Node v10 and NPM v6 sudo npm install -g npm grunt-cli npm install # if you do not want to create a release # just work on it # now the js and css is built on the fly # in the ./public folder npm run watch # if you have bash and want to create a full release # and strip all unneeded files, # optimize the packagist vendor folder # you might need zip from linux # the files will be in the ./build/release folder # and the zip is in the ./build/release/p3x-gitlist-a.b.c.zip file ./scripts/release.sh ``` [Install](INSTALL.md) - here. [//]: #@corifeus-footer --- [**P3X-GITLIST**](https://pages.corifeus.com/gitlist) Build v2018.12.13-4 [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) ## P3X Sponsors [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![NoSQLBooster](https://cdn.corifeus.com/assets/png/nosqlbooster-70x70.png)](https://www.nosqlbooster.com/) [The Smartest IDE for MongoDB](https://www.nosqlbooster.com) [//]: #@corifeus-footer:end artifacts/000077500000000000000000000000001516067322700130475ustar00rootroot00000000000000artifacts/config.ini000066400000000000000000000033061516067322700150170ustar00rootroot00000000000000[git] client = '/usr/bin/git' ; Your git executable path default_branch = 'master' ; Default branch when HEAD is detached repositories[] = '../git-test' ; Path to your repositories ; If you wish to add more repositories, just add a new line ; WINDOWS USERS ;client = '"C:\Program Files (x86)\Git\bin\git.exe"' ; Your git executable path ;repositories[] = 'C:\Path\to\Repos\' ; Path to your repositories ; You can hide repositories from GitList, just copy this for each repository you want to hide ; hidden[] = '/home/git/repositories/BetaTest' [app] debug = false cache = false title = "" ; big files can block the browser in code mirror, so there is a limit codemirror_full_limit = 48 [clone_button] ; ssh remote show_ssh_remote = true ; display remote URL for SSH ssh_user = 'git' ; user to use for cloning via SSH ; http remote show_http_remote = true ; display remote URL for HTTP use_https = true ; generate URL with https:// ; if cloning via GIT ; we cannot find out the subdir ; so we have to add this git clone subdir ; eg git_clone_subdir = '/var/git/repository/' git_clone_subdir = '/var/git/repository/' ; has to end with trailing slash http_user = '' ; user to use for cloning via HTTP (default: none) http_user_dynamic = false ; when enabled, http_user is set to $_SERVER['PHP_AUTH_USER'] ; If you need to specify custom filetypes for certain extensions, do this here [filetypes] ; extension = type ; dist = xml ; If you need to set file types as binary or not, do this here [binary_filetypes] ; extension = true ; svh = false ; map = true ; set the timezone [date] timezone = Europe/Budapest format = 'Y.m.d. H:i:s' ; custom avatar service [avatar] ; url = '//gravatar.com/avatar/' ; query[] = 'd=identicon'artifacts/config.windows.ini000066400000000000000000000033621516067322700165120ustar00rootroot00000000000000[git] ;client = '/usr/bin/git' ; Your git executable path default_branch = 'master' ; Default branch when HEAD is detached ;repositories[] = '../git-test' ; Path to your repositories ; If you wish to add more repositories, just add a new line ; WINDOWS USERS client = 'C:\Program Files\Git\bin\git.exe' ; Your git executable path repositories[] = 'C:\Users\patrikx3\Projects\patrikx3\gitlist-workspace\gitlist\git-test' ; Path to your repositories ; You can hide repositories from GitList, just copy this for each repository you want to hide ; hidden[] = '/home/git/repositories/BetaTest' [app] debug = false cache = false title = "P3X Gitlist Windows" ; big files can block the browser in code mirror, so there is a limit codemirror_full_limit = 48 [clone_button] ; ssh remote show_ssh_remote = true ; display remote URL for SSH ssh_user = 'git' ; user to use for cloning via SSH ; http remote show_http_remote = true ; display remote URL for HTTP use_https = true ; generate URL with https:// ; if cloning via GIT ; we cannot find out the subdir ; so we have to add this git clone subdir ; eg git_clone_subdir = '/var/git/repository/' git_clone_subdir = '' ; has to end with trailing slash http_user = '' ; user to use for cloning via HTTP (default: none) http_user_dynamic = false ; when enabled, http_user is set to $_SERVER['PHP_AUTH_USER'] ; If you need to specify custom filetypes for certain extensions, do this here [filetypes] ; extension = type ; dist = xml ; If you need to set file types as binary or not, do this here [binary_filetypes] ; extension = true ; svh = false ; map = true ; set the timezone [date] timezone = Europe/Budapest format = 'Y.m.d. H:i:s' ; custom avatar service [avatar] ; url = '//gravatar.com/avatar/' ; query[] = 'd=identicon'artifacts/php-7.2-ubuntu.md000066400000000000000000000023751516067322700160130ustar00rootroot00000000000000[//]: #@corifeus-header # 🤖 P3X Gitlist - An enhanced elegant, feature rich and modern git ui repository viewer [//]: #@corifeus-header:end # Install PHP 7.2 on Ubuntu before Bionic ```bash sudo add-apt-repository ppa:ondrej/php # enter (empty) sudo apt update sudo apt install -y php7.2 sudo apt upgrade -y ``` [//]: #@corifeus-footer --- [**P3X-GITLIST**](https://pages.corifeus.com/gitlist) Build v2018.12.13-4 [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) ## P3X Sponsors [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![NoSQLBooster](https://cdn.corifeus.com/assets/png/nosqlbooster-70x70.png)](https://www.nosqlbooster.com/) [The Smartest IDE for MongoDB](https://www.nosqlbooster.com) [//]: #@corifeus-footer:endboot.php000066400000000000000000000012211516067322700125370ustar00rootroot00000000000000mount('', new GitList\Controller\MainController()); $app->mount('', new GitList\Controller\BlobController()); $app->mount('', new GitList\Controller\CommitController()); $app->mount('', new GitList\Controller\TreeController()); $app->mount('', new GitList\Controller\NetworkController()); $app->mount('', new GitList\Controller\TreeGraphController()); $app->mount('', new GitList\Controller\GitController()); return $app; changelog.md000066400000000000000000000143771516067322700133540ustar00rootroot00000000000000[//]: #@corifeus-header # 🤖 P3X Gitlist - An enhanced elegant, feature rich and modern git ui repository viewer [//]: #@corifeus-header:end ## v2018.12.13 * The icons of the main tabs animations were not centered (cosmetic fix) * There was a routing problem, some pages were not working * Added tooltips to the log, which is not shown - too long text ... ## v2018.9.20-0 * Sort the repo list by last updated ## v2.12 * Treegraph is using markdown ## v2.11 * Markdown images ware not working 100% * File extension types were not working with like eg. JPG, Jpg, jPg, now it works * On the index page it shows the last commit and username by elapsed time ## v2.10 * Upload binary and/or existing files * Able to delete binary files * Added some animation to the Fontawesome 5 icons * Add new text files or add directory * Sometimes, the delete file, it was not showing the last, but the previous last commit * Markdown, twemoji on commits * Fix long commits * Display SVG files, while you can edit it as well * The network dots were small, now big ones with 10 radius, 20px; * Delete files * grunt-contrib-less - 2 is missing the `@path` variable * add change log display * The right GitHub menu is now a popup menu instead of a link to GitHub. ## v2.9 * The tree search and the blames are using the shared code (with option to use with Code mirror) * Diffs are rendering with web workers. * Minor typo on the next/older commit page * Synched to klaussilveira/gitlist * Added twemoji to the commits list * Memory limit * Updated NPM and composer * The Gitter is merged into Gitlist to code easier * The pager was not working with the browser back function * The commit message is using markdown ## v2.6.0 * network and graph works with Bootstrap 3 totally ## v2.5.0 * you can edit files now ## v2.0.12-585 * removed `Babel` 😀 ## v2.0.4-579 * Automated build with webpack and grunt ## v1.1.18-573 * for big commits and changing the theme, it calculated the time it was loading the full commit list and it is about the same time when you change the theme, it added an overlay and this text eg. `9 seconds to switch the theme` * automatic versions generated with grunt ## v1.1.14 * added overlay when loading big commits ## v1.1.12 * allow CodeMirror show toggle height 300px vs all ## v1.1.11 * Make to use the tag/branch button like GitHub ## v1.1.10 * total dark/light mode (CodeMirror, diff, Markdown) ## v1.1.9 * markdown dark mode * we automatically load the next commit, when we are on the bottom of the page * minor toast fix ## v1.1.8 The `config.ini` file with `url_subdir` or later `clone_subdir` variable has been changed to the `git_clone_subdir` variable. ## v1.1.7 The following `CodeMirror` syntax highlighting has been added: ```js require('codemirror/mode/cmake/cmake'); require('codemirror/mode/css/css'); require('codemirror/mode/dockerfile/dockerfile'); require('codemirror/mode/go/go'); require('codemirror/mode/handlebars/handlebars'); require('codemirror/mode/htmlmixed/htmlmixed'); require('codemirror/mode/javascript/javascript'); require('codemirror/mode/jsx/jsx'); require('codemirror/mode/perl/perl'); require('codemirror/mode/php/php'); require('codemirror/mode/powershell/powershell'); require('codemirror/mode/python/python'); require('codemirror/mode/ruby/ruby'); require('codemirror/mode/sass/sass'); require('codemirror/mode/shell/shell'); require('codemirror/mode/sql/sql'); require('codemirror/mode/swift/swift'); require('codemirror/mode/twig/twig'); require('codemirror/mode/vue/vue'); require('codemirror/mode/xml/xml'); require('codemirror/mode/xquery/xquery'); require('codemirror/mode/yaml/yaml'); ``` ## v1.1.6 * All `PHP` files will be in the `root` and only `index.php`, `images`, `icons`, `svg`, `css`, `js`bundle files will be in the `public` subdir. ## v1.1.5 * Removed old `clone` button modal popup, using `Bootstrap` only * Double checked, all is using pure `Boostrap` * Added `toast` and save the `url` to the clipboard when you click on the `clone` button * Added `UglifyJs` and minimize the `css`. ## v1.1.3 * Moved to `webpack` * Using `Babel` ## v1.1.2 * Added twemoji's ## v1.1.1 * Format size was missing space (ugly) * Graph time was not using the ```config.ini``` * Fixed images to not show a html block span text and use now real image alt and title attributes in html * Graph was not using Bootstrap * Network wast not using Bootstrap ## v1.0.3 * Total bytes was not working with Twig 2 ## v1.0.2 * Add support for .gitmodules files at repository root * Updated to latest dependencies ## v1.0.1 * The minimum PHP version is 7.1 and PHPUNIT 7. ## v1.0.0 * Works with ```PHP 7.2``` ## v0.5.6 * The Markdown image links were not working. * Missed out the ```package.json``` from the previous release. ## v0.5.5 * Fixed PHPUNIT 6 ## v0.5.4 * Different submodule links for Gitlist and Github ## v0.5.3 * The markdown links are working right * Shows submodules ## v0.5.2 * Added all Bootsswatch themes (https://bootswatch.com/) * Removed default theme, kept only Bootstrap (though like over 10 themes now) * Removed PHP 5 support, only >= 7 * Upgraded Silex v1 to v2 * Upgraded Twig v1 to v2 * Upgraded Symfony/twig-bridge v2 to v3 * Upgraded Symfony/filesystem v2 to v3 * Upgraded Phpunit v4 to v6 * Moved from Showdown to Marked (more features) * For building requires (not required for the server): * NodeJs >= 8.9.0 * Bower * Grunt [//]: #@corifeus-footer --- [**P3X-GITLIST**](https://pages.corifeus.com/gitlist) Build v2018.12.13-4 [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) ## P3X Sponsors [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![NoSQLBooster](https://cdn.corifeus.com/assets/png/nosqlbooster-70x70.png)](https://www.nosqlbooster.com/) [The Smartest IDE for MongoDB](https://www.nosqlbooster.com) [//]: #@corifeus-footer:endcomposer.json000066400000000000000000000014461516067322700136160ustar00rootroot00000000000000{ "name": "patrikx3/gitlist", "description": "An elegant git repository viewer", "minimum-stability": "stable", "autoload": { "psr-4": { "GitList\\": "src/GitList", "Gitter\\": "src/Gitter" } }, "require": { "php": ">=7.1.0", "symfony/filesystem": ">=4", "symfony/twig-bridge": ">=4", "twig/twig": ">=2", "silex/silex": ">=2", "symfony/process": ">=4", "danielstjules/stringy": ">=3", "spatie/temporary-directory": "^1.1", "eloquent/pathogen": "^0.6.1" }, "require-dev": { "symfony/filesystem": ">=4", "mockery/mockery": ">=1", "symfony/browser-kit": ">=4", "phpunit/phpunit": ">=7", "symfony/css-selector": ">=4" } } composer.lock000066400000000000000000003122531516067322700135760ustar00rootroot00000000000000{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], "content-hash": "803d129999a50e41fba7adfbee3870ea", "packages": [ { "name": "danielstjules/stringy", "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/danielstjules/Stringy.git", "reference": "df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/danielstjules/Stringy/zipball/df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e", "reference": "df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e", "shasum": "" }, "require": { "php": ">=5.4.0", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "type": "library", "autoload": { "psr-4": { "Stringy\\": "src/" }, "files": [ "src/Create.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Daniel St. Jules", "email": "danielst.jules@gmail.com", "homepage": "http://www.danielstjules.com" } ], "description": "A string manipulation library with multibyte support", "homepage": "https://github.com/danielstjules/Stringy", "keywords": [ "UTF", "helpers", "manipulation", "methods", "multibyte", "string", "utf-8", "utility", "utils" ], "time": "2017-06-12T01:10:27+00:00" }, { "name": "eloquent/pathogen", "version": "0.6.1", "source": { "type": "git", "url": "https://github.com/eloquent/pathogen.git", "reference": "baa1376e1de56ad462cd67794de808d690904fb8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/eloquent/pathogen/zipball/baa1376e1de56ad462cd67794de808d690904fb8", "reference": "baa1376e1de56ad462cd67794de808d690904fb8", "shasum": "" }, "require": { "ext-mbstring": "*", "icecave/isolator": "~2", "php": ">=5.3" }, "require-dev": { "eloquent/liberator": "~2", "icecave/archer": "~1" }, "type": "library", "autoload": { "psr-4": { "Eloquent\\Pathogen\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Erin Millard", "email": "ezzatron@gmail.com", "homepage": "http://ezzatron.com/" }, { "name": "Darian Brown", "email": "darianbr@gmail.com", "homepage": "http://www.darian-brown.com/" } ], "description": "General-purpose path library for PHP.", "homepage": "https://github.com/eloquent/pathogen", "keywords": [ "file", "filesystem", "manipulation", "path" ], "time": "2014-10-22T01:04:35+00:00" }, { "name": "icecave/isolator", "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/IcecaveStudios/isolator.git", "reference": "97c51fafa39c57a8f1a31f978a48fbe6cea4a5d5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/IcecaveStudios/isolator/zipball/97c51fafa39c57a8f1a31f978a48fbe6cea4a5d5", "reference": "97c51fafa39c57a8f1a31f978a48fbe6cea4a5d5", "shasum": "" }, "require": { "php": ">=5.3" }, "require-dev": { "icecave/archer": "~1" }, "suggest": { "eloquent/asplode": "Drop-in exception-based error handling." }, "type": "library", "autoload": { "psr-4": { "Icecave\\Isolator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "James Harris", "email": "james.harris@icecave.com.au", "homepage": "https://github.com/jmalloc" } ], "description": "Dependency injection for global functions.", "homepage": "https://github.com/IcecaveStudios/isolator", "keywords": [ "fake", "mock", "phake", "phpunit", "test", "unit" ], "time": "2014-08-12T03:16:11+00:00" }, { "name": "pimple/pimple", "version": "v3.2.3", "source": { "type": "git", "url": "https://github.com/silexphp/Pimple.git", "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32", "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32", "shasum": "" }, "require": { "php": ">=5.3.0", "psr/container": "^1.0" }, "require-dev": { "symfony/phpunit-bridge": "^3.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.2.x-dev" } }, "autoload": { "psr-0": { "Pimple": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" } ], "description": "Pimple, a simple Dependency Injection Container", "homepage": "http://pimple.sensiolabs.org", "keywords": [ "container", "dependency injection" ], "time": "2018-01-21T07:42:36+00:00" }, { "name": "psr/container", "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", "homepage": "https://github.com/php-fig/container", "keywords": [ "PSR-11", "container", "container-interface", "container-interop", "psr" ], "time": "2017-02-14T16:28:37+00:00" }, { "name": "psr/log", "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], "time": "2018-11-20T15:27:04+00:00" }, { "name": "silex/silex", "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/silexphp/Silex.git", "reference": "6bc31c1b8c4ef614a7115320fd2d3b958032f131" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/silexphp/Silex/zipball/6bc31c1b8c4ef614a7115320fd2d3b958032f131", "reference": "6bc31c1b8c4ef614a7115320fd2d3b958032f131", "shasum": "" }, "require": { "php": ">=7.1.3", "pimple/pimple": "^3.0", "symfony/event-dispatcher": "^4.0", "symfony/http-foundation": "^4.0", "symfony/http-kernel": "^4.0", "symfony/routing": "^4.0" }, "replace": { "silex/api": "self.version", "silex/providers": "self.version" }, "require-dev": { "doctrine/dbal": "^2.2", "monolog/monolog": "^1.4.1", "swiftmailer/swiftmailer": "^5", "symfony/asset": "^4.0", "symfony/browser-kit": "^4.0", "symfony/config": "^4.0", "symfony/css-selector": "^4.0", "symfony/debug": "^4.0", "symfony/doctrine-bridge": "^4.0", "symfony/dom-crawler": "^4.0", "symfony/expression-language": "^4.0", "symfony/finder": "^4.0", "symfony/form": "^4.0", "symfony/intl": "^4.0", "symfony/monolog-bridge": "^4.0", "symfony/options-resolver": "^4.0", "symfony/phpunit-bridge": "^3.2", "symfony/process": "^4.0", "symfony/security": "^4.0", "symfony/serializer": "^4.0", "symfony/translation": "^4.0", "symfony/twig-bridge": "^4.0", "symfony/validator": "^4.0", "symfony/var-dumper": "^4.0", "symfony/web-link": "^4.0", "twig/twig": "^2.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.3.x-dev" } }, "autoload": { "psr-4": { "Silex\\": "src/Silex" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Igor Wiedler", "email": "igor@wiedler.ch" } ], "description": "The PHP micro-framework based on the Symfony Components", "homepage": "http://silex.sensiolabs.org", "keywords": [ "microframework" ], "abandoned": "symfony/flex", "time": "2018-04-20T05:17:01+00:00" }, { "name": "spatie/temporary-directory", "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/spatie/temporary-directory.git", "reference": "5e1799fa2297363ebfb4df296fea90afbd4ef9b7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/5e1799fa2297363ebfb4df296fea90afbd4ef9b7", "reference": "5e1799fa2297363ebfb4df296fea90afbd4ef9b7", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.3" }, "type": "library", "autoload": { "psr-4": { "Spatie\\TemporaryDirectory\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Alex Vanderbist", "email": "alex@spatie.be", "homepage": "https://spatie.be", "role": "Developer" } ], "description": "Easily create, use and destroy temporary directories", "homepage": "https://github.com/spatie/temporary-directory", "keywords": [ "spatie", "temporary-directory" ], "time": "2018-04-12T09:34:43+00:00" }, { "name": "symfony/contracts", "version": "v1.0.2", "source": { "type": "git", "url": "https://github.com/symfony/contracts.git", "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", "shasum": "" }, "require": { "php": "^7.1.3" }, "require-dev": { "psr/cache": "^1.0", "psr/container": "^1.0" }, "suggest": { "psr/cache": "When using the Cache contracts", "psr/container": "When using the Service contracts", "symfony/cache-contracts-implementation": "", "symfony/service-contracts-implementation": "", "symfony/translation-contracts-implementation": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\": "" }, "exclude-from-classmap": [ "**/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "A set of abstractions extracted out of the Symfony components", "homepage": "https://symfony.com", "keywords": [ "abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards" ], "time": "2018-12-05T08:06:11+00:00" }, { "name": "symfony/debug", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", "reference": "e0a2b92ee0b5b934f973d90c2f58e18af109d276" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/debug/zipball/e0a2b92ee0b5b934f973d90c2f58e18af109d276", "reference": "e0a2b92ee0b5b934f973d90c2f58e18af109d276", "shasum": "" }, "require": { "php": "^7.1.3", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": "<3.4" }, "require-dev": { "symfony/http-kernel": "~3.4|~4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", "time": "2018-11-28T18:24:18+00:00" }, { "name": "symfony/event-dispatcher", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", "reference": "921f49c3158a276d27c0d770a5a347a3b718b328" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/921f49c3158a276d27c0d770a5a347a3b718b328", "reference": "921f49c3158a276d27c0d770a5a347a3b718b328", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/contracts": "^1.0" }, "conflict": { "symfony/dependency-injection": "<3.4" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0", "symfony/stopwatch": "~3.4|~4.0" }, "suggest": { "symfony/dependency-injection": "", "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", "time": "2018-12-01T08:52:38+00:00" }, { "name": "symfony/filesystem", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", "reference": "2f4c8b999b3b7cadb2a69390b01af70886753710" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/2f4c8b999b3b7cadb2a69390b01af70886753710", "reference": "2f4c8b999b3b7cadb2a69390b01af70886753710", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", "time": "2018-11-11T19:52:12+00:00" }, { "name": "symfony/http-foundation", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", "reference": "1b31f3017fadd8cb05cf2c8aebdbf3b12a943851" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1b31f3017fadd8cb05cf2c8aebdbf3b12a943851", "reference": "1b31f3017fadd8cb05cf2c8aebdbf3b12a943851", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { "predis/predis": "~1.0", "symfony/expression-language": "~3.4|~4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", "time": "2018-11-26T10:55:26+00:00" }, { "name": "symfony/http-kernel", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", "reference": "b39ceffc0388232c309cbde3a7c3685f2ec0a624" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b39ceffc0388232c309cbde3a7c3685f2ec0a624", "reference": "b39ceffc0388232c309cbde3a7c3685f2ec0a624", "shasum": "" }, "require": { "php": "^7.1.3", "psr/log": "~1.0", "symfony/contracts": "^1.0.2", "symfony/debug": "~3.4|~4.0", "symfony/event-dispatcher": "~4.1", "symfony/http-foundation": "^4.1.1", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/config": "<3.4", "symfony/dependency-injection": "<4.2", "symfony/translation": "<4.2", "symfony/var-dumper": "<4.1.1", "twig/twig": "<1.34|<2.4,>=2" }, "provide": { "psr/log-implementation": "1.0" }, "require-dev": { "psr/cache": "~1.0", "symfony/browser-kit": "~3.4|~4.0", "symfony/config": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", "symfony/css-selector": "~3.4|~4.0", "symfony/dependency-injection": "^4.2", "symfony/dom-crawler": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", "symfony/process": "~3.4|~4.0", "symfony/routing": "~3.4|~4.0", "symfony/stopwatch": "~3.4|~4.0", "symfony/templating": "~3.4|~4.0", "symfony/translation": "~4.2", "symfony/var-dumper": "^4.1.1" }, "suggest": { "symfony/browser-kit": "", "symfony/config": "", "symfony/console": "", "symfony/dependency-injection": "", "symfony/var-dumper": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\HttpKernel\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", "time": "2018-12-06T17:39:52+00:00" }, { "name": "symfony/polyfill-ctype", "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.9-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "ctype", "polyfill", "portable" ], "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.9-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", "mbstring", "polyfill", "portable", "shim" ], "time": "2018-09-21T13:07:52+00:00" }, { "name": "symfony/process", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", "reference": "2b341009ccec76837a7f46f59641b431e4d4c2b0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/2b341009ccec76837a7f46f59641b431e4d4c2b0", "reference": "2b341009ccec76837a7f46f59641b431e4d4c2b0", "shasum": "" }, "require": { "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Process Component", "homepage": "https://symfony.com", "time": "2018-11-20T16:22:05+00:00" }, { "name": "symfony/routing", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", "reference": "649460207e77da6c545326c7f53618d23ad2c866" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/routing/zipball/649460207e77da6c545326c7f53618d23ad2c866", "reference": "649460207e77da6c545326c7f53618d23ad2c866", "shasum": "" }, "require": { "php": "^7.1.3" }, "conflict": { "symfony/config": "<4.2", "symfony/dependency-injection": "<3.4", "symfony/yaml": "<3.4" }, "require-dev": { "doctrine/annotations": "~1.0", "psr/log": "~1.0", "symfony/config": "~4.2", "symfony/dependency-injection": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0", "symfony/http-foundation": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0" }, "suggest": { "doctrine/annotations": "For using the annotation loader", "symfony/config": "For using the all-in-one router or any loader", "symfony/dependency-injection": "For loading routes from a service", "symfony/expression-language": "For using expression matching", "symfony/http-foundation": "For using a Symfony Request object", "symfony/yaml": "For using the YAML loader" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Routing\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Routing Component", "homepage": "https://symfony.com", "keywords": [ "router", "routing", "uri", "url" ], "time": "2018-12-03T22:08:12+00:00" }, { "name": "symfony/twig-bridge", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", "reference": "2e928d6c8244e7f3b32bcfac5814095a83179e60" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/2e928d6c8244e7f3b32bcfac5814095a83179e60", "reference": "2e928d6c8244e7f3b32bcfac5814095a83179e60", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/contracts": "^1.0.2", "twig/twig": "^1.35|^2.4.4" }, "conflict": { "symfony/console": "<3.4", "symfony/form": "<4.2", "symfony/translation": "<4.2" }, "require-dev": { "symfony/asset": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", "symfony/form": "^4.2", "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", "symfony/routing": "~3.4|~4.0", "symfony/security": "~3.4|~4.0", "symfony/security-acl": "~2.8|~3.0", "symfony/stopwatch": "~3.4|~4.0", "symfony/templating": "~3.4|~4.0", "symfony/translation": "~4.2", "symfony/var-dumper": "~3.4|~4.0", "symfony/web-link": "~3.4|~4.0", "symfony/workflow": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0" }, "suggest": { "symfony/asset": "For using the AssetExtension", "symfony/expression-language": "For using the ExpressionExtension", "symfony/finder": "", "symfony/form": "For using the FormExtension", "symfony/http-kernel": "For using the HttpKernelExtension", "symfony/routing": "For using the RoutingExtension", "symfony/security": "For using the SecurityExtension", "symfony/stopwatch": "For using the StopwatchExtension", "symfony/templating": "For using the TwigEngine", "symfony/translation": "For using the TranslationExtension", "symfony/var-dumper": "For using the DumpExtension", "symfony/web-link": "For using the WebLinkExtension", "symfony/yaml": "For using the YamlExtension" }, "type": "symfony-bridge", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Bridge\\Twig\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Twig Bridge", "homepage": "https://symfony.com", "time": "2018-12-06T10:57:56+00:00" }, { "name": "twig/twig", "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", "reference": "6a5f676b77a90823c2d4eaf76137b771adf31323" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/twigphp/Twig/zipball/6a5f676b77a90823c2d4eaf76137b771adf31323", "reference": "6a5f676b77a90823c2d4eaf76137b771adf31323", "shasum": "" }, "require": { "php": "^7.0", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "psr/container": "^1.0", "symfony/debug": "^2.7", "symfony/phpunit-bridge": "^3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.5-dev" } }, "autoload": { "psr-0": { "Twig_": "lib/" }, "psr-4": { "Twig\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com", "homepage": "http://fabien.potencier.org", "role": "Lead Developer" }, { "name": "Armin Ronacher", "email": "armin.ronacher@active-4.com", "role": "Project Founder" }, { "name": "Twig Team", "homepage": "https://twig.symfony.com/contributors", "role": "Contributors" } ], "description": "Twig, the flexible, fast, and secure template language for PHP", "homepage": "https://twig.symfony.com", "keywords": [ "templating" ], "time": "2018-07-13T07:18:09+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.2.x-dev" } }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", "homepage": "http://ocramius.github.com/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://github.com/doctrine/instantiator", "keywords": [ "constructor", "instantiate" ], "time": "2017-07-22T11:58:36+00:00" }, { "name": "hamcrest/hamcrest-php", "version": "v2.0.0", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", "shasum": "" }, "require": { "php": "^5.3|^7.0" }, "replace": { "cordoval/hamcrest-php": "*", "davedevelopment/hamcrest-php": "*", "kodova/hamcrest-php": "*" }, "require-dev": { "phpunit/php-file-iterator": "1.3.3", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "hamcrest" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], "time": "2016-01-20T08:20:44+00:00" }, { "name": "mockery/mockery", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", "reference": "100633629bf76d57430b86b7098cd6beb996a35a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/mockery/mockery/zipball/100633629bf76d57430b86b7098cd6beb996a35a", "reference": "100633629bf76d57430b86b7098cd6beb996a35a", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "~2.0", "lib-pcre": ">=7.0", "php": ">=5.6.0" }, "require-dev": { "phpunit/phpunit": "~5.7.10|~6.5|~7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-0": { "Mockery": "library/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", "homepage": "http://blog.astrumfutura.com" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", "homepage": "http://davedevelopment.co.uk" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", "homepage": "https://github.com/mockery/mockery", "keywords": [ "BDD", "TDD", "library", "mock", "mock objects", "mockery", "stub", "test", "test double", "testing" ], "time": "2018-10-02T21:52:37+00:00" }, { "name": "myclabs/deep-copy", "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { "php": "^7.1" }, "replace": { "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" }, "files": [ "src/DeepCopy/deep_copy.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", "keywords": [ "clone", "copy", "duplicate", "object", "object graph" ], "time": "2018-06-11T23:09:50+00:00" }, { "name": "phar-io/manifest", "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "phar-io/version": "^2.0", "php": "^5.6 || ^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Library for handling version information and constraints", "time": "2018-07-08T19:19:57+00:00" }, { "name": "phpdocumentor/reflection-common", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { "php": ">=5.5" }, "require-dev": { "phpunit/phpunit": "^4.6" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ "src" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jaap van Otterdijk", "email": "opensource@ijaap.nl" } ], "description": "Common reflection classes used by phpdocumentor to reflect the code structure", "homepage": "http://www.phpdoc.org", "keywords": [ "FQSEN", "phpDocumentor", "phpdoc", "reflection", "static analysis" ], "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "reference": "94fd0001232e47129dd3504189fa1c7225010d08" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", "reference": "94fd0001232e47129dd3504189fa1c7225010d08", "shasum": "" }, "require": { "php": "^7.0", "phpdocumentor/reflection-common": "^1.0.0", "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { "doctrine/instantiator": "~1.0.5", "mockery/mockery": "^1.0", "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.x-dev" } }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ "src/" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Mike van Riel", "email": "me@mikevanriel.com" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "time": "2017-11-30T07:14:17+00:00" }, { "name": "phpdocumentor/type-resolver", "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { "mockery/mockery": "^0.9.4", "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ "src/" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Mike van Riel", "email": "me@mikevanriel.com" } ], "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.8.x-dev" } }, "autoload": { "psr-0": { "Prophecy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", "homepage": "http://everzet.com" }, { "name": "Marcello Duarte", "email": "marcello.duarte@gmail.com" } ], "description": "Highly opinionated mocking framework for PHP 5.3+", "homepage": "https://github.com/phpspec/prophecy", "keywords": [ "Double", "Dummy", "fake", "mock", "spy", "stub" ], "time": "2018-08-05T17:53:17+00:00" }, { "name": "phpunit/php-code-coverage", "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", "php": "^7.1", "phpunit/php-file-iterator": "^2.0", "phpunit/php-text-template": "^1.2.1", "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^3.1 || ^4.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { "phpunit/phpunit": "^7.0" }, "suggest": { "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "6.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "time": "2018-10-31T16:06:48+00:00" }, { "name": "phpunit/php-file-iterator", "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "050bedf145a257b1ff02746c31894800e5122946" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", "reference": "050bedf145a257b1ff02746c31894800e5122946", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "time": "2018-09-13T20:33:42+00:00" }, { "name": "phpunit/php-text-template", "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "time": "2018-02-01T13:07:23+00:00" }, { "name": "phpunit/php-token-stream", "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Wrapper around PHP's tokenizer extension.", "homepage": "https://github.com/sebastianbergmann/php-token-stream/", "keywords": [ "tokenizer" ], "time": "2018-10-30T05:52:18+00:00" }, { "name": "phpunit/phpunit", "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "c23d78776ad415d5506e0679723cb461d71f488f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c23d78776ad415d5506e0679723cb461d71f488f", "reference": "c23d78776ad415d5506e0679723cb461d71f488f", "shasum": "" }, "require": { "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "myclabs/deep-copy": "^1.7", "phar-io/manifest": "^1.0.2", "phar-io/version": "^2.0", "php": "^7.1", "phpspec/prophecy": "^1.7", "phpunit/php-code-coverage": "^6.0.7", "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.0", "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", "sebastian/environment": "^4.0", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0", "sebastian/version": "^2.0.1" }, "conflict": { "phpunit/phpunit-mock-objects": "*" }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-soap": "*", "ext-xdebug": "*", "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "7.5-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], "time": "2018-12-12T07:20:32+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { "php": "^7.1", "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.0", "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff", "udiff", "unidiff", "unified diff" ], "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/febd209a219cea7b56ad799b30ebbea34b71eb8f", "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^7.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], "time": "2018-11-25T09:31:21+00:00" }, { "name": "sebastian/exporter", "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { "php": "^7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "time": "2017-04-03T13:19:02+00:00" }, { "name": "sebastian/global-state", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "time": "2017-04-27T15:39:26+00:00" }, { "name": "sebastian/object-enumerator", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { "php": "^7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "time": "2017-08-03T12:35:26+00:00" }, { "name": "sebastian/object-reflector", "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", "time": "2017-03-29T09:07:27+00:00" }, { "name": "sebastian/recursion-context", "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "time": "2017-03-03T06:23:57+00:00" }, { "name": "sebastian/resource-operations", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "time": "2018-10-04T04:07:39+00:00" }, { "name": "sebastian/version", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, "require": { "php": ">=5.6" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/browser-kit", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", "reference": "db7e59fec9c82d45e745eb500e6ede2d96f4a6e9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/browser-kit/zipball/db7e59fec9c82d45e745eb500e6ede2d96f4a6e9", "reference": "db7e59fec9c82d45e745eb500e6ede2d96f4a6e9", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/dom-crawler": "~3.4|~4.0" }, "require-dev": { "symfony/css-selector": "~3.4|~4.0", "symfony/process": "~3.4|~4.0" }, "suggest": { "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", "time": "2018-11-26T11:49:31+00:00" }, { "name": "symfony/css-selector", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", "reference": "aa9fa526ba1b2ec087ffdfb32753803d999fcfcd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/css-selector/zipball/aa9fa526ba1b2ec087ffdfb32753803d999fcfcd", "reference": "aa9fa526ba1b2ec087ffdfb32753803d999fcfcd", "shasum": "" }, "require": { "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jean-François Simon", "email": "jeanfrancois.simon@sensiolabs.com" }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", "time": "2018-11-11T19:52:12+00:00" }, { "name": "symfony/dom-crawler", "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", "reference": "7438a32108fdd555295f443605d6de2cce473159" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7438a32108fdd555295f443605d6de2cce473159", "reference": "7438a32108fdd555295f443605d6de2cce473159", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "symfony/css-selector": "~3.4|~4.0" }, "suggest": { "symfony/css-selector": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.2-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\DomCrawler\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", "time": "2018-11-26T10:55:26+00:00" }, { "name": "theseer/tokenizer", "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": "^7.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "time": "2017-04-07T12:08:54+00:00" }, { "name": "webmozart/assert", "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.6", "sebastian/version": "^1.0.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.3-dev" } }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ "assert", "check", "validate" ], "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.1.0" }, "platform-dev": [] } config.example.ini000066400000000000000000000033711516067322700144730ustar00rootroot00000000000000[git] client = '/usr/bin/git' ; Your git executable path default_branch = 'master' ; Default branch when HEAD is detached repositories[] = '/var/git/' ; Path to your repositories ; If you wish to add more repositories, just add a new line ; WINDOWS USERS ;client = 'C:\Program Files (x86)\Git\bin\git.exe' ; Your git executable path ;repositories[] = 'C:\Path\to\Repos\' ; Path to your repositories ; You can hide repositories from GitList, just copy this for each repository you want to hide ; hidden[] = '/home/git/repositories/BetaTest' [app] title = P3X GitList debug = false cache = true ; big files can block the browser in code mirror, so there is a limit codemirror_full_limit = 48 [clone_button] ; ssh remote show_ssh_remote = true ; display remote URL for SSH ssh_user = 'git' ; user to use for cloning via SSH ; http remote show_http_remote = true; display remote URL for HTTP use_https = true ; generate URL with https:// ; if cloning via HTTP is triggered using virtual dir (e.g. https://example.com/git/repo.git) ; it there is a subdir, start with string and end with trailing slash ; eg git_clone_subdir = '/var/git/repository/' git_clone_subdir = '' http_user = '' ; user to use for cloning via HTTP (default: none) http_user_dynamic = false ; when enabled, http_user is set to $_SERVER['PHP_AUTH_USER'] ; If you need to specify custom filetypes for certain extensions, do this here [filetypes] ; extension = type ; dist = xml ; If you need to set file types as binary or not, do this here [binary_filetypes] ; extension = true ; svh = false ; map = true ; set the timezone [date] ; timezone = UTC ; format = 'd/m/Y H:i:s' ; custom avatar service [avatar] ; url = '//gravatar.com/avatar/' ; query[] = 'd=identicon'gitlist.iml000066400000000000000000000011051516067322700132460ustar00rootroot00000000000000 package.json000066400000000000000000000047021516067322700133600ustar00rootroot00000000000000{ "name": "p3x-gitlist", "version": "2018.12.13-5", "corifeus": { "prod-dir": "prod", "postfix": "3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de", "prefix": "p3x-", "publish": false, "type": "p3x", "opencollective": false, "cdn": true, "icon": "fas fa-code", "code": "Spawn", "nodejs": "v11.4.0", "reponame": "gitlist", "build": true }, "scripts": { "build": "grunt && webpack --production", "webpack-watch": "webpack --watch", "less-watch": "grunt watch:less", "watch": "grunt && concurrently \"webpack --watch\" \"grunt watch:less\"" }, "description": "🤖 P3X Gitlist - An enhanced elegant, feature rich and modern git ui repository viewer", "main": "index.js", "repository": "https://github.com/patrikx3/gitlist", "author": "patrikx3 ", "license": "MIT", "devDependencies": { "bootswatch": "^3.3.7", "concurrently": "^4.1.0", "corifeus-builder": "^2018.12.9-7", "corifeus-utils": "^2018.12.9-7", "css-loader": "^2.0.0", "extract-text-webpack-plugin": "^4.0.0-beta.0", "file-loader": "^2.0.0", "fs-extra": "^7.0.1", "glob": "^7.1.3", "grunt": "^1.0.3", "grunt-contrib-less": "^2.0.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "less-loader": "^4.1.0", "on-build-webpack": "^0.1.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "postcss-safe-parser": "^4.0.1", "style-loader": "^0.23.1", "terser-webpack-plugin": "^1.1.0", "url-loader": "^1.1.2", "webpack": "^4.27.1", "webpack-cli": "^3.1.2" }, "engines": { "node": ">=10.13.0" }, "homepage": "https://pages.corifeus.com/gitlist", "dependencies": { "@fortawesome/fontawesome-free": "^5.6.1", "bootstrap": "^3.3.7", "codemirror": "^5.42.0", "highlight.js": "^9.13.1", "is-string-int": "^1.0.1", "jquery": "^3.3.1", "jquery.redirect": "^1.1.4", "js-cookie": "^2.2.0", "js-htmlencode": "^0.3.0", "list.js": "^1.5.0", "lodash": "^4.17.11", "marked": "^0.5.2", "moment": "^2.23.0", "php-date": "^2.1.1", "raphael": "^2.2.7", "snackbarjs": "^1.1.0", "twemoji": "^11.2.0" } }phpunit.xml000066400000000000000000000011661516067322700133040ustar00rootroot00000000000000 ./src/ ./tests/ public/000077500000000000000000000000001516067322700123455ustar00rootroot00000000000000public/.htaccess000066400000000000000000000005131516067322700141420ustar00rootroot00000000000000 Options -MultiViews +SymLinksIfOwnerMatch RewriteEngine On #RewriteBase /path/to/gitlist/ RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [L,NC] order allow,deny deny from all public/img/000077500000000000000000000000001516067322700131215ustar00rootroot00000000000000public/img/favicon.png000066400000000000000000001336361516067322700152700ustar00rootroot00000000000000PNG  IHDRo'sBIT|d pHYs+tEXtSoftwarewww.inkscape.org< IDATx{u}߳9IB.ԠE *x^f\4g*jLVqflkfVq/mDDIձ3" *pS `8JIrs޿N'9y'Z}B (Rwug !/DM r#@=sO))! !5!#C+B#Žo}x՗86h:hUg?n$_Zpjc!\bRݾ樭_r"]~ٝv"!hc(>.§V_-Bv@- 0O?:b-B|]aC=?py pu1P9B ~h(BxI=tC  =euR ▍c'pz=R;b7|hݺ)S@8ݡ5~{BOKG!s}xwU!c7o<+"'7B5xm!P9ٛ={}wvwBhg1xP1۫/é@J9Y(_֧2`?#/@*9YnX>>.8MԳsi(™TA͝Pvߔz R+/n X V׎o~/$3T!xƷ"l|G(Bxk6/:@Fٻ>BxS-uR5;VgSo~heX$bqڣ0z {)GGcA_X7>.nXz 4cgU>B8?&!\njwKzI Qh"K$irA &=q|DrB 4Dr@ DrN j3ǫE$rjE&: qDrF zE$.r*M':,qDrN f2ǛE$r*Eo&* qDrF <T@@rx^DrB  GP6G$ r&@@8!%0p8'@@H@ 9#s("9&08!0H9}'"9"W8!09}#"9&8 O9='K"9"S8 9=#O"9&8 K9K&3H"9"$8)@$T9"S"9K!`8U"X9 "SE"9!0o8U&P9"S"9 !pX8u"0_9$SG"9!PJDrG q@$PrDIDr<8N@~8M&p 8NDrO @'+"9sr̉H$  k892%H; C8H/ 38qX:rǡwDrFI hqG$h!q?YrapDrjNA 1qO )qM eXt5@DrjFI qK$&q>^rǡ>Dr*NA 0qK$>q?r ǡ9Dr*FI qK$"qh>Zr !"9@uR]qx7?Gh:6M$n8z @ H(8£/~u[G_v]@yI@&CVDrro"9dE$HG CHYh>q|+CVDrd!q|!+"9` 8NJ`pr>[Js`0r>Esr>esr>Gsrgk?rDkrdkrH"9dE$`RDrȊH9T!"CVDrJq|HyF X*9"9E$XVucl. !P9k>pCI"M#ks7,On X!צr8^C^$X8``aryc#C^Drq|HyG 8&9"9E$8<D"@pMsDrȋHPN 8@"@899"9E$x2199"9E$x" r {99"9E$x@dM1"@'!/"9;Ȓ8^N$@r ;ȕ@dE?"9l 'C^Dr 79q|DrȋHD O_:"F{G$@r!/"9t9Hxh2hqDrȋH4@48>8"9E$H C<"M# #C^DrIrDrȋH4@Ԛ8^"9E$@ jK"u'$WHy:ǫO$@] @!C^Drr6!/"9P79P x}G$HyA;l#SB$@_;)!C^Dr`ro!/"9o9sDrJ'%9"9%DrȋH@T"9E$A zq|HN "&=ѷ8>G$Hy^%{#SB$@ ,)!C^Drr`!/"9T9("9E$B ,y#SB$b T&)!C^Dr`1r`*!/"9P90/sDrJX89"9%DrȋH̗@Rm"9E$C J.)!C^Drprj!/"9p(9$sDrJ(#OИ8>G$Hy!/"9p !48)!C^Drrq|HN "sr\6q|HN "!"9E$rTq|HN "@rPq|HN "@rȌ8~"9E$< qHN "@~rȄ8~"9%DrȋHy <!/"9C H$Hy 948H"9%DrȋH'@CK$SB$l948#"9%DrȋH%@È=&SB$L948'"9%DrȋH#@C}&SB$,948> "9%DrȋH!@͉&SB$ 9Ԙ8HN "@ PSxb"9%DrȋH&@ !SB$P_9Ԍ8^1"9%DrȋH$@%SB$P?9Ԅ8^q"9%DrȋH"@ 5!SB$P9T8^3"9%DrȋH @5%SB$P}9T8^s"9%DrȋH&@ !SB$P]9T80"9%DrȋH$@ %SB$P=9T8p"9%DrȋH"@)!C^Dr3#SB$P 9$$gJ$Hy =3'SB$@ DrJ`q@$Hy HDrJO 9$"9E$`qE$Hy`pr3q)!C^Dr HgQDrJO >Y"9E$qzB$Hyr1q)!C^DrzH/DrJzO +"9E$qB$HywrX"q)!C^Dr @' "9E$`q)!C^DrXADrJO ǩ"9E$`q*I$Hy`r'qJ)!C^DrXADrJO ǩ"9E$qjI$Hyr(!Sk"9%DrȋH&A4HN "@9 ("9%DrȋH'4HN " qF)!C^Drx"8N&DrJ~L  {8Y)!C^Drx@@q$SB$ 1q!/"9Ȓ8A$Hyș@@vqx"9E$ W9Y DrJ dCC)!C^Drr#qA$Hyȉ@@"9%DrȋH@.rME)!C^Drr X8,HN "t9$C!/"9M&88HN "T9"C!/"9M$8HN "49 !/"9M"P{8 HN "9l+. !nNQq}쪡SR$ :~bcQGM돛j=QGwuGa'z1z%q*@$D;拱wN>e}XyĆH]!:w춫\:߼YX*q*D$by7^g/{Z>+$'z NӹeG|3Bgv8$ rjq0/i>ҽ曦.Q7]z=P^P/98Ԁ4]?vw=%2/rjKro6nɏoUkH=KrA Ҽ\Luj>.zQtRρ:,/ǡƼ$''{/_죞p /6JrKrr<0kߧSρ*qD$'GwL}cS*DrJ RUrnLʼn7k|@58@T`^ٛv|ƸwϪc 5/J!"933{|/HRU8B:k]Ro[Z8:ڞ㐉_6{˶)O85 JҊ S/ o^*1V tUavfY1s+&*s+Bܳ]5NLqntX`UBέ@Xyk>v[-s+9 $έ@!tzCyE!s+ 008PJ$B_GRoDrM ` Mr!wή愸s|}- I!#VW9[ 7rVXwHNe˟?i=)!lYU$rFE$GO[g^>丹) Iqٛn8-HMr rzq'$Gő.˧RO4$ Uz(~䪡򒜼vݻS &+>Ǿ[hǁQv΃z k9=8wnã:3sĝSO4$w `MrxT{r/hɔ@8 T;]$'{O.")!lYU$`r-n8<1{gq`"Z^߿2HaȺK▍éP_9 s+Ba%^; VX`U*(~䪡N S &+>,,8TBQ[K-ߛz &9%0on&9bȳWCN57X yqs47! v#7X78,gUpn]Cq[ %7//8$gUZqnSVロz %PY[!sqO;{+zs++*@9Bbw7̊8{M)s+<84ܹ/Q:v;3 6%9#n&9^:tڙU&9erssh$7W{: [] IDATN(zT<@@Y[!SŪ?lFPέp @Ý3s/?.,K=‡;9@&99j?}|- 7#dq Kn紖-ߛzTrVȚhuo. 7p C8c$''}/]GROq _^dqqc򛾑zT28AINFMMr pV$'ᓇ_mg@UI 8'7H hgUȹi;G@8<9@ @6l P7"9@58INôO|O;n$hHMr(?[Sπzr rrV !Vhe?7@]9P9@  3^pL Pg"9@ 5#THN+xnzW"9@} 5⃜X$|&j+nO=·;I  o|z,^'hUz4]¦[ErjjnŪݡhzB9C Hqk=So^ia"ȅ $8@3>vw +q'Lyq 5 gU){So^i?zƹru̱^ڣ G"9@:9 /nib W"9@9 '@3֭O=zgdBB> 0x9 '@sFROi 3"$Ý7?} @õZn6!'V[q+ZC'@9TH09@9* 9Z9T'(tRO&9@? =8@fHt U%9@=8@~n 39T ڳ;ҝ|¼$=Wgrt{&gROKr^('fqLi'V6*,@Ϊwh'v85 @o !}W?gDVo~qtw] +ᇖ,K#,<^zJ;֦,%,r${'1L=z=Ԕw,@0O8ffN=z`Oر>`Drq%ؑz,ROMrqg;Kydl,W$/U`>:lJ*}T @80?9@ q|Q7R- F$8< `!:wvrz,EI=7M 8,T 3={F@IPF xgUXC?z,Vms+'`Y!kqXKr W9/='fugiyIL 8չ̲;*ȓ@d9}W|)7|M_N G9gUٛw&|&9h,7Hs׭Ԟ[SR5_3jsȁ@48@jxX PfƯ:K$N 9}[Tpq>1>|h2h*⮉#wݐz(?3n|h&h j&?t1!z<ޏ'ENyr:8os=3W};rh=7`./Į?K&9$EK8uSWX>;\nutH=h&+>Ǿ[.K>@/yI4@-y9@Z\}wXlC)d*vuV @?xIԙ@x9@t{#g}6q/Ɂ:/5G>#2әofg4@yAԆ8@So ?{ǁA(B4>vk[F */n8<1V\Bܜz ,ž;Bg}o扱R8:ڞ4EO -ۮܲ;\crVN5_yW|٩P}~fEܽ& o> T@%4Z31BNb_8P> T@Trm T+@x9@N+'ݗ˞z ?ƟqN=@έU9P8{S![h-U PE9P Ϊ[nz]_rK1X esF~פTHN=OmO~![j:7ϺONj%L5*3s 6Ѐ8BWVG9 @ DmOŋq89zo0[[ҍ;֧2_#oyו%q|c|e` S/ }GGw^B[NĎ|ުܟz n]o}w>#y˻\3zH&9h$Yw@?8^Ϻ{jyhOqƃ7]z)󵈗}WM}O^ޓQ@Xٻ(C{SSL 4-$"CLb qJH4^r8DLKLDq\8!ћAeh'z~? }Qꡪ9\]Ԯg_&_՝y(8cYۧН{vMi`Q+M!P rif 'P\ =S_nuGiܙi+UBƈt[5<]YqPYoB,d|k }B)ϼOЬ00̭HϼN-2':nVգ4G`@ZV"_6*TN⍢ٻEU8GJM<roV, _;Ywr9~%9P: rF9&JLv44*G}Egpښ]j놦o[^wP5ї%-!I.>meˠ?(%i*o乓.)$pJrCAC(@3N'|qe+&eLJ&'m\{qn$N";ɼƼi}ܐI %uj;KG?a(t9vSӦB^; 5`ȴoWƟ=Xv G×|}Yx7EnIjnZ{c}e$Lںx*x+W\.?~HwSꀳsѧ/zmPIKwL0?a( rBMf'6Uy,`oUE%uganxe9Q O^8̹U:r˚?Y_Z`+rO ssd|a#M~J[/l@/ r8992BHY; >O)鉕-{,gY}_&0YP<\@Īw^{{eÄbs=?{-xΑe$pN |r2 הGV}+gΥM?DT;1"yMSN_vc`-$pJr(ʏRG ֝M?.l^{,FRe*GWǴXeD(';J5Xtط{obژH|{o-7uK|^BI0䀟Q>D9ҥrs!e,'A?3E2/*-m 8*%=%m+I8[TR֖B$ɺ}BΎɪR`%@9)CI93\ i&ʽN $0|v!g{qq'6\ op4W.q 4tm՝Hc$І r'( dAQmo! G΂ hmZxW{&kkztxPhCI 9^VI=A\h2M'uG:rC($mEp^ViB9/tG:rdxgDyA'.RaYd9`0qH/|ւ=g ;8@wdJh=䤏tG:r($Pj`}|Ru,`|ւ; 4Po8 H%?{-xq*:ŭ<%򹢞 $ҏ䀁Դiv}zr*[Up帏IY?VŐ-oQ O>RՈy$ r0̪@zY{ H&̼,TU}2'Iw@fUan0 9`fU ͤT2YT2ZwT$wXO7U rB qA]YJr Q@o*={\[wWpYs%969lt16XoBi)\$tՓw>+DE9GVR{92*?E9*l\;Gc rCU_v"oD9mWo\;Ĭk10̭^ ̪@fX={W!e,0BSM$wl`V[І[8Ax8d|l78(/jv ;@9̭pHѳ$p96 srd0{c]I wP.8d]ZV!r u$wBAr@ɒ{;YwK`YҾUsݜknisZA0b4VanЏЀr+ NC?;_T$7%96^@Q@vY{ V 6̭GA8GpܤUBZ=tEʢi_X;@In4Jrm(Ɂ qpWtD˛tg9$7ڰI9lNWw#ACIn46Mr (ȁNbVQ[Bt:e啺c,̭m[qpnBH9 }6`JrQhCIt 9Al{N8<)|`&6ɍ&96lGAtnv3ҫl0Fca$:h'fUdQ~a)IҲJҲ*9`.V 6̭GA8_I)Q@Jrc(ȁo:Iw+&tg$7ڰI9p lw؃t=lMrah r(U)Gw+daQ_̭m[R rE#2\IT$7%96QA9c>qu |R JrQhCIo$p!'xu:tؠ;;ŝpq'pd'+0 @:\i4.p.>̪> @:_̭m[>G9g蝣;Vy3(ɍFI %9(kl! tGAIn46a88C3ia&$p6 r*`iE²1![1s+0 Pa"yMB^/8hP(+ly"DEuoН8Fc@6W 6L*ӝH'ug>MrI0l( ̪d DYE9"-!tR<%K%>)g.~R #sՌAYt Դiv}zTu,J63 O4w'ɍ%SO$9ԥu5b'a rx*o9O՝Hjj,,V 6̭$4qBH E29Z]; tg2hPk(zTU!@W9ӝ$JrQhCI/ Qڥ)O8N1.Q=@Q@Jrx9\rno @Wz>oP@Jrx9\IOl}r^ΖM3]Ԡ;MK{ Jr}R\;GcGBAQӦ5Q!ԥ#[3]_ ;mN$dx$pF$9܈¬ -;e ܊ј[І9\rvoWtJ&w'w,ӝЅhPÍ( lbWHު;Fc@66Ўq@Dtg:#_17`hl8 p rhŬ zxΈB [0b4VannAAm(ܷEt|V;&F$Іn@A-d.ꄧ$6e86ɍ&96lC7 rdLZ=Dwo8In4NhIrt'!/K,[j% _i=0뚰jj(#Npŋ8TޞZ;%՝3Np!u7c5I!ԥ2'7H*՝>TM8=@«ιӄYv*:ŭ

WS]!\ם RRӦMB\; ]TK uG{K U eoTF/#ʼn@afy]#NvڵJw/pLJYG$8ݵLwM_9L쥹[1 O>KV,h Nnzυ7@%] ^a !;0ڨMwŝFNmZYw~Cw9SnRܥ; eKǝP; ̓X6FwGw$gnP̭BJqj欫8"b#:vI Eɤ>U2h(0jmY0A" 5.4w8$)Z˟ r|Ne e,@f)+W.L Zf]{@SwsNV ŝ̭RX /{u{uPSWZ9 )xP$wOg,7^dZ6ɍ&9CBJ3:턇gцrӈK).֝lJZ> |Ke~a?Y]jͲ;7A0cxFc!X_wizI!_<?;P?Px(8UםQtg5 \s&R/l}ɺJ)![>_wC!D1=B>"(~Ҕ!D(r0C'&?K⯿8VwȽoȲUκ<C}rq's+*[YW Џ !/8=pX8a[Vk3tx3xgG+bgD,ɹP\ ܘۇ}u@/ rS)螲ug- ^>KdA1?qTjƛ.'Lq{opY18wR꓋;)BʓZ_lIm췅k{Ͽ|zQ^\.Y3!@ F$пuXֻWPvb( B( xY/|"yq85{bٯι%y"Y%9pKr6 II&9WRwM.IYEASJ:cRf . )tFJ7=uuGi̪ s+@ ̭ǤCﺢJwdO4)RwνRzfsiTcW&־?\wJ1r4̭)0b4Vg]?GAS~H _wB_*tԸ2_wdRuMoLZv(YanH1?g}úS (}vQRXНQŗ^e-By*Ysk9k;J{upVh[R`nh̭4>{= (}a}oBLҝ/R%^iZaź sT tgi4P)P!)Tկ΁ %+7tjj(B,e ąR֏xi8Y]INn&9Fcտ;g?37ש؋s B}W{DEjnߊ/Puugi.n&9Fcn^z  rIJYBqs` D_y qukkh4GwJ0܊ј[|DJ4g}s {Ȯc+t$,-eonV^IY]pؒy=v$',Z[BL&jjLh4=ڿ']MѰ6 _ͷ#_!D,h'ky tDdg]pnK->Y=O ]x9r"; NE?;0۶y+CA#u$ؤ;գt=dI4ZCFXViY)Kd*G57$w\hM2zyѪɽK3AaU!2̥B9NunOZ>TwPBI@In4Jr1+v΁ KNa9t2j 8m=~r]`ĸճO,ה*ܝޖXygAqbT,ѝ2ྐྵeugAjjͲƛ8^5֗C(Ɂ(ɍFIN* rzCBktɲo ~n@!םZT}؊_XlvQkEh!%+}wZ3gis=ֹ_}In%9:NPHE?& #TR7uf!@9F6Z8H^e̛h/; F76tr6;KG?Hh䀹6[=tO4NݺsƜ*|ua'J;Odryݚ(Nt_BNѝ$0U?yolW $lSmC*]:5j;+TC]qtce"[qJ١ѿiE/n:E>I_ror6)JfuG_k.Xd?̈%>\$>XqG&M;كnղq 0tJT?-H{0]rpl)In46(t@QDԱ[sǟ~Mwpd,ƉE4K'lνEy{%lZ3Xwt0?Hhm)b< r:FCwpk-?oݣ72ٻkY/nܾq,^b|O#G75vC(Ɂ(ɍFIx۞y+Ju@QDԱ-Bhͽ g?Y]b'VW?b1~uD$)rŌ󦕉Pxq\!ʖ;Y55莓NP)Py+t@Q:@}Ic\w)8vʜU CaN2w4GMŋA6GǁGH;OV)TnYۃ9񥯏J6; ~ᢳt IDATuDyh [.rIε&R/l}ɺUO?2>Rڵs;=h$=7PΖuoɿ,Z8 2j }'dF) ugbʊkhO'Ɂ8In4N nOםEQ1nUO(vBw||tuo;s? ¶%ٶaKE^Q㧐|TBI@In4Jrc(X;/; !;I,ka?$ ~m}aCviㄴ}JqToM,}3{d ݑɇ!@ F$ .۞\{:Pa-WV{ҝ4Viߪۃ*Ezh TC}S>`bقr,.rJr JrQ%>AAuS!!, |ʜ[Z.?5S}Ϳ3N tb#zzܼ"auz\LԪTuexشY\bcoCI@In4Jr[{K c !*tL\/(xe+O/jgkȂ:Yܽˏ !)PWKcq[4"t j UCm139C99@ F$mKt@QD%cRѝβyw>(0l4!,jㅓQ(ǏHh[)t@Y Kج;]$)/O9n'^?y ;s?)Ǐʎ\}i^ [^;{ ! dxo޼@w!tG@vPHwH'{ENGo>7,~rwգt,@WEf~-x_ԝUߝEn{ -ݻTPH\}F3;pF>z0Uڷ[D(9INIC.'(}"HnН8tSo ; HZz|"sa fU V R(}q]ͳ}CMw q 'U; 42/t_Zg\-w(ǻ oHʥt|jCѼAA#-;t]ZVU3RXv,HSc_U EG¬JZY(Ɂcp>ZݟX20td9%9YW~RIvx,Q3Qatg XVҾUE9l)In46M$PmpXu":Nw~Y5xFI&dxg\;'$0#sUVvPwHɲCYP4Rw ξhu'z ZEf~-|ag̭Gpp eppd.2Mݞ_~ rmXARSuT?o[;KO(6\șU\ ŝFId/sW rz%ĵsΘVളԝg`t{+ Y?_Ϻwr8wrP20td9'Ɂ̒ʺis {(}U}^6]Q; Ip"ߺ:=hxd[V$R$8IdW̚;gf !f ++B; |UsOޜHiO bj9; ̗;s?S')Ɂ:xP20td9%9~J嬯~Cwd?$}('tgϱDOKwxz^tG(]Ŏ\ ^ [^zbBC,B$tgAψ|&Itg@q܇Pae_BpkvŐsÂ^oZ; +I&$J._N YB\;!=fª8S^ܶ~er޺,l@ lMr M%;g]F9}>5g]Gae_B0e@H^S};²< b7[1+ &d=pȷn!9 *&-تjvӝQ$R`hl9²欫zQL@YjְBugV7W vp;;/_/΁#O%κIn46Ɂc{B!@GB}s0_p9eIs?dQVsvn !!<}bAk 'C8A!w|5B+S+2Ų_>"_cOo{P(rL1Lz4\ ŝFNӔy@A6sUomxRSug`88j9CvĪ)19k'Z8~4l)In46ɁOH!(~99yGB>0䁀"QOFd?8&N@ $7'!>,**bۘB Sfo>Ͽ%Bu`- Wk*InHiڵ}E剂@إeU˿3X& .cjP; MrI&$asE=(.WT'q!BHKwfˏ;pս{ůUahl)In46aOWu'7Pf]1yJr"ʽ綰"Q#sNNqq'w;1< rt s%Bʈ(XE݊EdD46ɍ&9FcA9 :G<'p?)ɝOnT; 1܊ј[Q(%$Ɵn4<{sx^eF$R$7%9\rAA.cXYB\QJEP726ɍ&9Fc.C9 GZIcׅιBwݤuVXSKwMrI&$KP#(ȑ6̭Bׯ НƗQ;܊ј[R`nh̭@3q9Ҋ@?Ho9>(-Ҋh@ F$& r%9_2')rYH[E"ҝf$7%9%(ɑe rdw7yu/riNnݰ:Y 0w;h\܉,GfQ#c׮`_;)Jm/W0w;h\܉ GQ#[%[kdn~_9ΰ k/s$7%9%(AЇZI'22;2}!cdFcHMrIv^ІMr,RFtĶѧschl)In46ɑ8 V̭5EzM@Ixú[1s+@ ̭8܁Qgҥt!JUZPHh q9\Mr#m vN@:$֯ܜܶ/6ɍ&9FcP5$+8-FFpo^ $7@ lMrߣPU[)8A*tܚXBw1܊ј[-q9\.f НHڽ _p@ F$q9\WTZKya #$+= נ$7%9%(}rFAN;S*,AE[bŝFN .4wrGAWNB]/;*Us>~fsמ2:c>4MlCMlז8N 9gn7uћ|ڦA=t_TVB3VB3R;8q C}g;`·;`DDrCkC'0Drg=:q~Q'h->4HH"yh"yq wB5N8q?;`'y|qgh2|qgh281 ;a:S{㪾a?S>|qgh2|qghsq ީw3`f>Ms>τan%4s+an%4s+##@NX"9~{E#*lek3<3`DDrCɇN'>lL_>ܙ0KAs^G>DŽd<4a<4C#9$ј៸s1~ z;w1a$&9d$&㴇@N+[?O}Ŧv7~mSw0[ d[ 㴋@Nk0\#1:;`-;sLx"yh"9d䡉k&>9bNT}g>P>1`<4a<4&N9cuEsXշs3/-R(5lf2lf|qK ̭Pt:~rՇj]_ Ufn%4s+an%4s+}i7a_uOT [lHH"yh"y89&ÀML>5xAN#^O_ZK$M$ <4|I8@"9 PǏ^w;y}cGsUUC!C$M$q0a`m+^V)]wFP!&8)'`L̻0 [6m^ &CHH.Ëaz욺ǽ Qfwvx"yh"9d䡵8qնmo:{;EqV@Ӕ;?n{~٢9[/e̾O|lCQ~Ctm=('})HH0C[ErXHW_rr!Gә8Su7toicnj8KaWmywy^%9BYM^[^r_iCm_n]%yXc_%9,{zIVg NiKrq#̭t矘 U'wan%4s+an%̭#2DrXޣ?SWt:U*=~N,݉OC!C$-p$ǡ9d$Uu'&-cG%{~4U7^^6CI6C I.CrMrXgvN'U},g#\$6CI6C I.J's+2GzO}QU;vFQ~20ZqVJ a%+2aӦ/,7UM%&CHZ#8! $76=Nrh3T}4MlCMI.j 6ɡ?gO;n; Uw` 6CI6ù(+:85#C $3/8Yw؉UC=wݾu^]~%qH$%$&9d$CȡDrZ_OM;:T}RgGulgUүE"9d䡉!785'Ca󛿼w^16֘pގ]n񓪾_DrC!C$o,q@ irϮo?v\Q=ToGT-# CHH"y94HNە{昅?6waUCEr|Uէkq2DDrCȡ!ڶM'qvo眢(Ϊ-ܽ7T鬫F,cy{T}JV8 ̷vʠ۶RB$f$_}wn,/Pu$C Wmywy^%9mU>ݗ.ܼѩstF,?Xo7Ͻ5&/$/aiݻn9Kk/aI8KZǡrhs+]S՝?zLU= Y{rGLά~ `n2̭fn2̭Ԏ8 %C[ycT}CҝxxEW}J0VB3VjCȡ̭z[?,uםX9 Vgם~]IUү!̭@̭@ʉp94Zon/L\ 'a/rϮUү!̭@̭@ʈ@ gnu'㧞~O+D&+nu.LV}LF0VB3VFN r 뿲|ⱛ'/~EsXBekߧ>~̧7 Z#[ s+[ s+##C 9an/LS/Sܻ痯\Xz9U߲̭@̭@!1EQu_s_w/;FsݿszqṶ@̭@! 1EQtszyp):#>C~ݵnwsVqs+an%4s+aneqJ $zy/䶱#ʶ&O~_}eGeu)Ǐp޽?zao'T}j5(`n2̭fn2̭Mȡ%$=bهu:cGT}Sӕގٿ-{~gwq/!Kм$ /ɳqhZKrXD7޽f&'<t|J_Ϟe+12$Kr|I8PyVKrXB7޽[է'8Wjo~n쎽oXgd'U8~%yh^C/"CK B^2z݉n킢(÷N߾5{[|)ǯ|[/!}{~}Շ@]M?|I@]w>y5|iՇ@]M|(B>@E?gsEշ@Stf&~g?37sNg}7 LY>Wxl}7?bfU4L_/;O8{D|uW ?hG$ǁ(ry^LLMLuGY4kƬ[}O-;g,rEw_ُ?%yh^CFK^ 0?3yeO^|WLc^Yt:y]=ovu*w=wLgZK"9d䡉k$(ʧv_toNs@^r䰴]%yXKrxNy;Wv>E*#2wn9U$ d[ JjB *')<42DDr8@jD jA$R"yh"9d䡉 5#!)<42DDrKjH jE$R"yh"9d䡉䴏8Ԕ@ԎHDDrCiq1%H䡉!&8Ԝ@ԖHDDrCɉK@ jM$R"yh"9d䡉# !')<42DDrǁȁFɁHH"yh"9' #!)<42DDrKH E$R"yh"9d䡉484@4HDDrCiqh0h$H䡉!&S8p9X"9C!C$M$q h4H䡉!&S?8@4HDDrCɩqD BɁHH"yh"9ǁ`r H䡉!&SqH BɁHH"yh"9'A @8"9C!C$M$gtq 0I$R"yh"9d䡉 8'a@J$M$ <4!\BBȁDr %&CHHt]By8D'@J$M$ <4ǁȁVɁHH"yh"9 "!)<42DDr@G ZE$R"yh"9d䡉䬂8@HDDrCYqh/h%H䡉!&qh7h-H䡉!& q@ ZM$R"yh"9d䡉,B( @$"&CHHNB8@ (Dr`"yh"9d䡉8r@J$M$ <4qC H䡉!&8"9C!C$M$oq`)9"Dr %&CHH 8rr%@J$M$ <4<4q G XHDDrCC!d@J$M$ <4<q_9@Dr %&CHH89@Dr %&CHHh8J + )<42DDFVC X!H䡉!&78Z9*@J$M$ <4qVI$R"yh"9d䡉&@"9C!C$M$%q`r5ɁHH"yh"y" HDDrCkA$`@Dr %&CHH^)q`rɁHH"yh"y%qaL$R"yh"9d䡉#% @0"9C!C$M$ q`r!ɁHH"yh"P& HDDrCɇB`Dr %&CHH>P8# )<42DDFI H䡉!&80j9@J$M$ <4|Uq*#&)<42DD"T@$R"yh"9d䡉}$TD$R"yh"9d䡉&TH$R"yh"9d䡉@ HDDrC"ԅ@P"9C!C$M$/B&Dr %&CHZ#8P79@@J$M$ #8f9@K@J$M$ z>s+@JhV JH_*N3[!ԇ@ADr %&CH8r^D$R"yh"9d!,I `Q"9C!C$o4qe ,I$R"yh"9d$%,H䡉!78@_rDr %&CH8}HDDrkM`Er&)<42DZX1ɁHH"y@@J$M$ qVM `UDr %&CH^)q5X5H䡉!WB`rD$R"yh"9d#%09k&)<42DɁHH"P @@J$M$ |(qN `Dr %&CH>P8C!0p"9C!C$q H䡉!8P HDDrWE`rJ$R"yh"9d+"09C')<42D@H@J$M$ |Y8#%02"9C!C$_8 HDDr"P ɁHH"yQ8HDDrhy$@@e^ϼ(HH-8HDDrhY$ʉ@J$M$DrqH䡉<Ԋ@@m@J$M$\vrjE$R"yh"9d8$P;"9C!#H$-ZɁHH 8&P["9C!\rjM$R"yh"9d4,4@@@J$M$DrqhH䡉QH.(9!)<42jqG QDr %&CF"8@# 4HDDrȨI$h,FɁHHGrqFh,H䡉QQ$h<FɁHH#8!4HDDrQ$C  H䡉1H.@@"9C!cH\ PDr %&Cƀ#8@H9@J$M$ErqrBɁHHk8 %)<42VqUIDATM$R"yh"9d0@@x"9C!H.9 )<422\UrZC$R"yh"9d,qZG UDr %&C!8-$:WmV^8sNQgU}P|l9Xs7umc@um=ӝy}U}@@+]]z@QEΛO< .>KrXJ#v;><_%P2̭fnչv Omh-VɁHH(rɁ䡉P8/Ɂ䡉䴜8)')<4P9$Dr %&28,F C@J$M$%qX@ɁHHNp8,G %@J$M$'(qrrXHDDr9d@J$M$'q%@Dr %&p8@}ɁHHNCR9HDDrF`Dr %&8@ )<4`-rX%H䡉Ԕ8k%@J$M$fqH$R"yh"95!0"9Cɩ8$@J$M$"8 @$)<4`r0H䡉䌈8"@J$M$gq&D$R"yh"9C" 0D"9C0qFA !ɁHH΀0*9HDDrHQ`DDr %&J8@#$)<4 9HDDr$@UrHDDr2q@ɁHHq@ɁHH!q*&)<4P9ԀHDDNr H䡉%@P#"9C[G:fDr %&8u%@ @J$M$O:Dr %&%@ Pc"9C r9H䡉a94HDDIrhH䡉%@ "9CG&aDr %&78M%@@J$M$=qL ɁHH^W'W\94HDD׬?#@ p"9CB0r@$R"yh"yqE  Dr %&WEprD$R"yh"@@J$M$q H䡉&@h9%)<4|XqO Dr %&8 @p"9CEDr %&8"@K@J$M$_-qZG ɁHHR8@-#)<4_8@-$)<4H䡵/"r "9CkO$%@J$-~$eDr %7@@,I$R"yh"8A %)<8\$Y"9Ck~$@J$\VH &)P@J$mp\FD N$R"yhk80B90"9C[}$ɁH#8T@ FJ$R"yhGrq@HD\*$ɁHґ\*&ɁHڋ#8Ԁ@TJ$R"yh/D{~[@-p򹝝Eo73ټ,_oy:_Bէa=n?-ʪo}zOܥEQU} sMQWv:/ZZɁH8Ԏ@ԎHDqrDr %78Ԗ@ԖHDFZZɁH(8P{9P{"9AA A$R"y@c@c@J$%qhhH"##)qrDr %WJK K$R"y%qrDr %84@4HD򑸶(+qr HC%a@"9ɇBBPDr %8#@J$qI BɁH&8@%)|Uq 4M$R"@x9HD@+@+@J$_8@HDE@@@J$?8@HD(qrDr H.%%Frqh5h5H,@ @@%\(r(Dr`#8<y"9 q@ɁTH.B 8HDrq`9"Dr H.,A XHqeɁT"8!d@!\@H<}$Frq`rɁT"8B9 @&\X`Dr Uq$VI X%HUq5@$R#8 k$Erq`rɁԐ#80 9@jH\ `Dr 5H. @0`"9P$@ H1C" HVq!H$R+8 C&>#809@*qFD$RKDrq`rɁ!\1`Dr }z?d\)@PCN>(]-@:׮?-8- IS[OEQo$T r [6&TL$6@ D &DrhqNr!2qnr!"qr!qr!qrɡqjN$&@ hDh !DrhqIrɡqF$:H h Dh*DrqrɡJ8@ 'CqrDr%q  FAD D$aaqrDr$q * (AL L$a5q6Z@$B h !@""9,GheDrX8F9@ J h)Bh7DrMh;DrI@ iqri q|HNl88HNL8/&""9,N `Q"91,M `I"9&<e48@@@HNG /"9 ?ԛ8HN=@ԋ8HN=@TK`mrD$8k'f"9%09!38#00"9%0X9%38'0p"9%09C!38#04"9k#0\9C%:8'0t"9+#09#!q@H,N`rFN$`8H~8Hv8HV8H2w@@--!P#9!'P39"%PC9##PS9$!Pc9ݽTe](86Ɛ@ 0/0cF+lBIbb($vnB0_1 ZHF#Ξ9g9M~y2Y"yˉ8&8@ qZH uD#R9$qh-a8-'j"yCq:@ Du&9 q |q:F SD! ט8@G tHFq:L  3Moi{y^&R.]Mmޗ䭦Oy7rNsAX?wSK4=M `l ^.o'jzˈ%2XرK-#ZbC`J<䱦2qc妇zN public/index.php000077500000000000000000000014431516067322700141720ustar00rootroot00000000000000get('date', 'timezone')) { date_default_timezone_set($config->get('date', 'timezone')); } $app = require '../boot.php'; $app->run(); public/web-worker/000077500000000000000000000000001516067322700144315ustar00rootroot00000000000000public/web-worker/commit-diff.js000066400000000000000000000027131516067322700171700ustar00rootroot00000000000000 const showNumber = (lineInfo) => { const first = lineInfo.line[0]; return first === ' ' || first === '@' || first === '-' || first === '+'; } let htmlEncode const construct = (data) => { const diffs = data.diffs for(let diffLineIndex in diffs.lines) { diffs.lines[diffLineIndex].line = htmlEncode(diffs.lines[diffLineIndex].line) } let result = ` ` for(let lineInfo of diffs.lines) { result += ` ` } result += `
Old    ${diffs.old}
   New ${diffs.new}
${showNumber(lineInfo) ? lineInfo['num-old'] : '  '} ${showNumber(lineInfo) ? lineInfo['num-new'] : '  '}
${lineInfo.line}
` return result; } onmessage = function(e) { eval(`htmlEncode = ${e.data.htmlEncode}`) const result = construct(e.data); postMessage(result) }public/web.config000066400000000000000000000057631516067322700143240ustar00rootroot00000000000000 scripts/000077500000000000000000000000001516067322700125565ustar00rootroot00000000000000scripts/init.cmd000066400000000000000000000014261516067322700142110ustar00rootroot00000000000000@echo off set CACHE=cache set GIT_TEST=git-test del /s /q %CACHE% rmdir /s /q %CACHE% mkdir %CACHE% del /s /q %GIT_TEST% rmdir /s /q %GIT_TEST% mkdir %GIT_TEST% copy artifacts\config.windows.ini .\config.ini pushd %GIT_TEST% for %%r in ("https://github.com/patrikx3/angular-compile" "https://github.com/patrikx3/onenote" "https://github.com/patrikx3/aes-folder" "https://github.com/patrikx3/ramdisk" "https://github.com/patrikx3/openwrt-insomnia" "https://github.com/patrikx3/gitlist" "https://github.com/patrikx3/gitlist-workspace" "https://github.com/patrikx3/resume-web" "https://github.com/patrikx3/service-manager-tray-for-windows" "https://github.com/patrikx3/docker-debian-testing-mongodb-stable") do ( echo %%r git clone --bare %%r ) popd composer install npm install scripts/init.sh000077500000000000000000000020371516067322700140620ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" set -e sudo rm -rf ./cache mkdir -p ./cache touch ./cache/.gitkeep chmod 0770 ./cache sudo rm -rf ./git-test/ mkdir -p ./git-test/ pushd ./git-test for repo in "https://github.com/patrikx3/angular-compile" "https://github.com/patrikx3/onenote" "https://github.com/patrikx3/aes-folder" "https://github.com/patrikx3/ramdisk" "https://github.com/patrikx3/openwrt-insomnia" "https://github.com/patrikx3/gitlist" "https://github.com/patrikx3/gitlist-workspace" "https://github.com/patrikx3/resume-web" "https://github.com/patrikx3/service-manager-tray-for-windows" "https://github.com/patrikx3/docker-debian-testing-mongodb-stable" do git clone --bare $repo done find . -name '*description*' | while read filename; do echo "$(dirname ${filename:2} | cut -f 1 -d '.') test repository." > $filename done popd sudo chmod 0777 ./git-test cp ./artifacts/config.ini ./ composer install chown $USER:$USER ./cache chown $USER:$USER ./git-test chown $USER:$USER ./config.ini scripts/optimize.sh000077500000000000000000000005041516067322700147540ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TOP=$DIR/.. set -e pushd $TOP sudo rm -rf ./cache mkdir -p ./cache touch ./cache/.gitkeep chmod 0770 ./cache composer install composer install --no-dev composer dump-autoload --optimize npm install npm run build --verbose rm -rf ./node_modules popdscripts/release.sh000077500000000000000000000020471516067322700145400ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TOP=$DIR/.. set -e version=$( node -e "console.log(require('$TOP/package.json').version)" ) pkg_name=$( node -e "console.log(require('$TOP/package.json').name)" ) name=$pkg_name-$version repo=$TOP/build/$name pushd $TOP npm install composer install --no-dev composer dump-autoload --optimize mkdir -p $TOP/cache #npm install npm run build rm -rf $repo || true mkdir -p $repo for item in "$TOP/cache" "$TOP/src" "$TOP/public" "$TOP/vendor" do echo $item cp -R $item $repo/ done for item in "$TOP/INSTALL.md" "$TOP/changelog.md" "$TOP/LICENSE" "$TOP/README.md" "$TOP/boot.php" "$TOP/config.example.ini" "$TOP/package.json" do echo $item cp $item $repo/ done rm -rf $repo/src/browser zipname=$TOP/build/$name.zip rm -rf $zipname pushd $repo #sudo apt install -y zip zip -r $TOP/build/$name.zip * .* > /dev/null popd RELEASE=$TOP/build/release rm -rf $RELEASE || true mv $repo $RELEASE cp $TOP/config.ini $RELEASE || true cp -R $TOP/git-test $RELEASE/ || true popd scripts/web.config000066400000000000000000000057631516067322700145350ustar00rootroot00000000000000 scripts/www-git-optimize.sh000077500000000000000000000003121516067322700163540ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TOP=$DIR/.. set -e pushd $TOP git checkout -f . git pull ./scripts/optimize.sh echo PWD: $PWD chown git:www-data -R $PWD popd src/000077500000000000000000000000001516067322700116565ustar00rootroot00000000000000src/GitList/000077500000000000000000000000001516067322700132355ustar00rootroot00000000000000src/GitList/Application.php000077500000000000000000000204671516067322700162250ustar00rootroot00000000000000path = realpath($root); $string = file_get_contents("../package.json"); $pkg = json_decode($string, true); $this['url_subdir'] = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); if ($this['url_subdir'] === '/') { $this['url_subdir'] = ''; } $this['debug'] = $config->get('app', 'debug'); $this['date.format'] = $config->get('date', 'format') ? $config->get('date', 'format') : 'd/m/Y H:i:s'; $this['theme'] = 'bootstrap'; $this['title'] = $config->get('app', 'title') ? $config->get('app', 'title') : 'P3X GitList ' . $pkg['version']; $this['filetypes'] = $config->getSection('filetypes'); $this['binary_filetypes'] = $config->getSection('binary_filetypes'); $this['cache.archives'] = $this->getCachePath() . 'archives'; $this['avatar.url'] = $config->get('avatar', 'url'); $this['avatar.query'] = $config->get('avatar', 'query'); $this['show_http_remote'] = $config->get('clone_button', 'show_http_remote'); $this['use_https'] = $config->get('clone_button', 'use_https'); $this['git_clone_subdir'] = $config->get('clone_button', 'git_clone_subdir'); $this['http_user'] = $config->get('clone_button', 'http_user_dynamic') ? $_SERVER['PHP_AUTH_USER'] : $config->get('clone_button', 'http_user'); $this['show_ssh_remote'] = $config->get('clone_button', 'show_ssh_remote'); $this['ssh_user'] = $config->get('clone_button', 'ssh_user'); // Register services $this->register(new TwigServiceProvider(), array( 'twig.path' => array($this->getThemePath($this['theme'])), 'twig.options' => $config->get('app', 'cache') ? array('cache' => $this->getCachePath() . 'views') : array(), )); $repositories = $config->get('git', 'repositories'); $this['git.projects'] = $config->get('git', 'project_list') ? $this->parseProjectList($config->get('git', 'project_list')) : false; $this->register(new GitServiceProvider(), array( 'config' => $config, 'git.client' => $config->get('git', 'client'), 'git.repos' => $repositories, 'ini.file' => "config.ini", 'git.hidden' => $config->get('git', 'hidden') ? $config->get('git', 'hidden') : array(), 'git.default_branch' => $config->get('git', 'default_branch') ? $config->get('git', 'default_branch') : 'master', )); $this->register(new ViewUtilServiceProvider()); $this->register(new RepositoryUtilServiceProvider()); $this->register(new RoutingUtilServiceProvider()); $this['twig'] = $this->extend('twig', function ($twig, $app) use ($pkg, $config) { $twig->addFilter(new \Twig_SimpleFilter('to_id', function($value) { $value = str_replace(['.', '/', '\\', ' '], '-', $value); $value = strtolower($value); return $value; })); $twig->addFilter(new \Twig_SimpleFilter('remove_extension', function ($string) { return pathinfo($string, PATHINFO_FILENAME); })); $twig->addFilter(new \Twig_SimpleFilter('htmlentities', 'htmlentities')); $twig->addFilter(new \Twig_SimpleFilter('md5', 'md5')); $twig->addFilter(new \Twig_SimpleFilter('format_date', array($app, 'formatDate'))); $twig->addFilter(new \Twig_SimpleFilter('format_size', array($app, 'formatSize'))); $twig->addFunction(new \Twig_SimpleFunction('avatar', array($app, 'getAvatar'))); $currentTheme = !isset($_COOKIE['gitlist-bootstrap-theme']) ? 'bootstrap-cosmo' : $_COOKIE['gitlist-bootstrap-theme']; $themeDark = [ 'cyborg', 'darkly', 'slate', 'superhero', 'solar', ]; $twig->addGlobal('theme_type', !in_array(substr($currentTheme, strlen('bootstrap-')), $themeDark) ? 'p3x-gitlist-light' : 'p3x-gitlist-dark'); $twig->addGlobal('theme', $currentTheme); $query = isset($_REQUEST['query']) ? $_REQUEST['query'] : (isset($_COOKIE['p3x-gitlist-query']) ? $_COOKIE['p3x-gitlist-query'] : ''); setcookie('p3x-gitlist-query',$query,0, '/' . $this['url_subdir']); $_COOKIE['p3x-gitlist-query'] = $query; $twig->addGlobal('search_query', $query); $twig->addGlobal('theme_postfix', $pkg['corifeus']['postfix']); $twig->addGlobal('prod_dir', $pkg['corifeus']['prod-dir']); $twig->addGlobal('theme_dark', $themeDark); $twig->addGlobal('version', $pkg['version']); $twig->addGlobal('gitlist_date_format', $this['date.format']); $codemirror_full_limit = (int)$config->get('app', 'codemirror_full_limit'); if (!is_int($codemirror_full_limit) || $codemirror_full_limit < 32) { $codemirror_full_limit = 32; } $twig->addGlobal('codemirror_full_limit', $codemirror_full_limit); return $twig; }); $this['escaper.argument'] = $this->factory(function() { return new Escaper\ArgumentEscaper(); }); // Handle errors $this->error(function (\Exception $e, $code) use ($app) { if ($app['debug']) { return; } return $app['twig']->render('error.twig', array( 'message' => $e->getMessage(), )); }); $this->finish(function () use ($app, $config) { if (!$config->get('app', 'cache')) { $fs = new Filesystem(); $fs->remove($app['cache.archives']); } }); } public function formatDate($date) { return $date->format($this['date.format']); } public function formatSize($size) { $mod = 1000; $units = array('B', 'kB', 'MB', 'GB'); for($i = 0; $size > $mod; $i++) $size /= $mod; return round($size, 2) . " " . $units[$i]; } public function getAvatar($email, $size) { $url = $this['avatar.url'] ? $this['avatar.url'] : "//gravatar.com/avatar/"; $query = array("s=$size"); if (is_string($this['avatar.query'])) $query[] = $this['avatar.query']; else if (is_array($this['avatar.query'])) $query = array_merge($query, $this['avatar.query']); $id = md5(strtolower($email)); return $url . $id . "?" . implode('&', $query); } public function getPath() { return $this->path . DIRECTORY_SEPARATOR; } public function setPath($path) { $this->path = $path; return $this; } public function getCachePath() { return $this->path . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; } public function getThemePath($theme) { return $this->path . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'twig' . DIRECTORY_SEPARATOR; } public function parseProjectList($project_list) { $projects = array(); $file = fopen($project_list, "r"); while ($file && !feof($file)) $projects[] = trim(fgets($file)); fclose($file); return $projects; } } src/GitList/Config.php000066400000000000000000000033371516067322700151610ustar00rootroot00000000000000validateOptions(); return $config; } public function __construct($data = array()) { $this->data = $data; } public function get($section, $option) { if (!array_key_exists($section, $this->data)) { return false; } if (!array_key_exists($option, $this->data[$section])) { return false; } return $this->data[$section][$option]; } public function getSection($section) { if (!array_key_exists($section, $this->data)) { return false; } return $this->data[$section]; } public function set($section, $option, $value) { $this->data[$section][$option] = $value; } protected function validateOptions() { $repositories = $this->get('git', 'repositories'); $atLeastOneOk = false; $atLeastOneWrong = false; foreach ($repositories as $directory) { if (!$directory || !is_dir($directory)) { $atLeastOneWrong = true; } else { $atLeastOneOk = true; } } if (!$atLeastOneOk) { die("Please, edit the config file and provide your repositories directory"); } if ($atLeastOneWrong) { die("One or more of the supplied repository paths appears to be wrong. Please, check the config file"); } } } src/GitList/Controller/000077500000000000000000000000001516067322700153605ustar00rootroot00000000000000src/GitList/Controller/BlobController.php000066400000000000000000000072771516067322700210300ustar00rootroot00000000000000get('{repo}/blob/{commitishPath}', function ($repo, $commitishPath) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); $blob = $repository->getBlob("$branch:\"$file\""); $breadcrumbs = $app['util.view']->getBreadcrumbs($file); $fileType = $app['util.repository']->getFileType($file); $binary = $app['util.repository']->isBinary($file) && $fileType !== 'image'; /* if ($fileType !== 'image' && $app['util.repository']->isBinary($file)) { return $app->redirect($app['url_generator']->generate('blob_raw', array( 'repo' => $repo, 'commitishPath' => $commitishPath, ))); } */ if (!$binary) { $output = $blob->output(); } else { $output = ''; } $extension = ''; $pathinfo = (pathinfo($file)); if (isset($pathinfo['extension'])) { $extension = $pathinfo['extension']; } return $app['twig']->render('file.twig', array( 'binary' => $binary, 'fileSize' => strlen($output), 'extension' => $extension, 'file' => $file, 'fileType' => $fileType, 'blob' => $output, 'repo' => $repo, 'breadcrumbs' => $breadcrumbs, 'branch' => $branch, 'branches' => $repository->getBranches(), 'browse_type' => 'blob', 'tags' => $repository->getTags(), 'enforceCodemirror' => isset($_GET['codemirror']) )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', '.+') ->convert('commitishPath', 'escaper.argument:escape') ->bind('blob'); $route->get('{repo}/raw/{commitishPath}', function ($repo, $commitishPath) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); $blob = $repository->getBlob("$branch:\"$file\"")->output(); $headers = array(); if ($app['util.repository']->isBinary($file)) { $headers['Content-Disposition'] = 'attachment; filename="' . $file . '"'; $headers['Content-Type'] = 'application/octet-stream'; } else { $headers['Content-Type'] = 'text/plain'; } return new Response($blob, 200, $headers); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->convert('commitishPath', 'escaper.argument:escape') ->bind('blob_raw'); return $route; } } src/GitList/Controller/CommitController.php000066400000000000000000000172551516067322700213770ustar00rootroot00000000000000get('{repo}/commits/search', function (Request $request, $repo) use ($app) { $subRequest = Request::create( '/' . $repo . '/commits/master/search', 'POST', array('query' => $request->get('query')) ); return $app->handle($subRequest, \Symfony\Component\HttpKernel\HttpKernelInterface::SUB_REQUEST); })->assert('repo', $app['util.routing']->getRepositoryRegex()); $route->get('{repo}/commits/{commitishPath}', function ($repo, $commitishPath) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($commitishPath === null) { $commitishPath = $repository->getHead(); } list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); $type = $file ? "$branch -- \"$file\"" : $branch; $pager = $app['util.view']->getPager($app['request_stack']->getCurrentRequest()->get('page'), $repository->getTotalCommits($type)); $commits = $repository->getPaginatedCommits($type, $pager['current']); $categorized = array(); foreach ($commits as $commit) { $date = $commit->getCommiterDate(); $date = $date->format('Y-m-d'); $categorized[$date][] = $commit; } $template = $app['request_stack']->getCurrentRequest()->isXmlHttpRequest() ? 'commits-list.twig' : 'commits.twig'; return $app['twig']->render($template, array( 'page' => 'commits', 'pager' => $pager, 'repo' => $repo, 'branch' => $branch, 'branches' => $repository->getBranches(), 'browse_type' => pathinfo($template)['filename'], 'tags' => $repository->getTags(), 'commits' => $categorized, 'file' => $file, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->value('commitishPath', null) ->convert('commitishPath', 'escaper.argument:escape') ->bind('commits'); $route->post('{repo}/commits/{branch}/search', function (Request $request, $repo, $branch = '') use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $query = $request->get('query'); $commits = $repository->searchCommitLog($query, $branch); $categorized = []; foreach ($commits as $commit) { $date = $commit->getCommiterDate(); $date = $date->format('Y-m-d'); $categorized[$date][] = $commit; } return $app['twig']->render('searchcommits.twig', array( 'repo' => $repo, 'branch' => $branch, 'file' => '', 'commits' => $categorized, 'branches' => $repository->getBranches(), 'browse_type' => 'searchcommits', 'tags' => $repository->getTags(), 'query' => $query )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('branch', $app['util.routing']->getBranchRegex()) ->convert('branch', 'escaper.argument:escape') ->bind('searchcommits'); $route->get('{repo}/commit/{commit}', function (Request $request, $repo, $commit) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $showLines = $request->get('ajax') === '1' || $app['request_stack']->getCurrentRequest()->isXmlHttpRequest(); $filename = $request->get('filename'); $commit = $repository->getCommit($commit, [ "showLines" => $showLines, 'filename' => $filename, ]); $branch = $repository->getHead(); if($request->get('ajax') === '1' || $app['request_stack']->getCurrentRequest()->isXmlHttpRequest()) { $diffsInstance = $commit->getDiffs(); $diffs = []; foreach($diffsInstance as $diffInstance) { $lines = []; foreach ($diffInstance->getLines() as $lineInstance) { $lines[] = (object)[ 'type' => $lineInstance->getType(), 'num-old' => $lineInstance->getNumOld(), 'num-new' => $lineInstance->getNumNew(), 'line' => $lineInstance->getLine(), ]; } $diffs[] = (object)[ 'binary' => $diffInstance->getBinary(), 'file' => $diffInstance->getFile(), 'old' => trim($diffInstance->getOld()), 'new' => trim($diffInstance->getNew()), 'index' => trim($diffInstance->getIndex()), 'lines' => $lines, ]; } return $app->json($diffs); }; if (isset($_GET['delete-branch'])) { $branch = $_GET['delete-branch']; } return $app['twig']->render('commit.twig', array( 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), 'browse_type' => 'commit', 'branch' => $branch, 'repo' => $repo, 'commit' => $commit, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commit', '[a-f0-9^]+') ->bind('commit'); $route->get('{repo}/blame/{commitishPath}', function ($repo, $commitishPath) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); $blames = $repository->getBlame("$branch -- \"$file\""); return $app['twig']->render('blame.twig', array( 'file' => $file, 'type' => $app['util.repository']->getFileType($file), 'binary' => $app['util.repository']->isBinary($file), 'repo' => $repo, 'branch' => $branch, 'branches' => $repository->getBranches(), 'browse_type' => 'blame', 'tags' => $repository->getTags(), 'blames' => $blames, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->convert('commitishPath', 'escaper.argument:escape') ->bind('blame'); return $route; } } src/GitList/Controller/GitController.php000066400000000000000000000073011516067322700206610ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace GitList\Controller; use Gitlist\Application as GitlistApp; use Gitter\Repository; use Silex\Application; use Silex\Api\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; class GitController implements ControllerProviderInterface { public function connect(Application $app) { $route = $app['controllers_factory']; $route->post('{repo}/git-helper/{branch}/{action}', function (Request $request, $repo, $branch = '', $action) use ($app) { $repository = ($app['git']->getRepositoryFromName($app['git.repos'], $repo)); $hadError = false; try { if ($repository instanceof Repository && $app instanceof GitlistApp) { $filename = trim($request->get('filename')); $value = $request->get('value'); $email = $request->get('email'); $name = $request->get('name'); $comment = $request->get('comment'); switch($action) { case 'save': $objectResult = $repository->changeFile($app->getCachePath(), $repo, $branch, $filename, $value, $name, $email, $comment); return json_encode($objectResult); case 'delete': $objectResult = $repository->deleteFile($app->getCachePath(), $repo, $branch, $filename, $name, $email, $comment); return json_encode($objectResult); break; case 'new-file-or-directory': $objectResult = $repository->newFileOrDirectory($app->getCachePath(), $repo, $branch, $filename, $name, $email, $comment); return json_encode($objectResult); break; case 'file-binary': $objectResult = $repository->newFileBinary($app->getCachePath(), $repo, $branch, $filename, $name, $email, $comment, $request->get('override') === '1' ? true : false, $_FILES['upload-file']); return json_encode($objectResult); /* return json_encode((object)[ 'filename' => $filename, 'email' => $email, 'name' => $name, 'comment' => $comment, 'upload-file' => $_FILES['upload-file'], 'override' => $request->get('override'), ]); */ break; default: return json_encode((object) [ 'status' => 'error', 'error' => true, 'message' => 'Un-implemented action "' . $action . '".' , ]); } } } catch(\Throwable $e) { $hadError = $e; } finally { if ($hadError !== false) { return json_encode((object) [ 'status' => 'error', 'error' => true, 'message' => $hadError->getMessage(), ]); } } }); return $route; } } src/GitList/Controller/MainController.php000066400000000000000000000131211516067322700210170ustar00rootroot00000000000000post('/json-error', function (Request $request) use ($app) { return $app['twig']->render('error.twig', array( 'error' => $request->get('error'), )); })->bind('json-error'); $route->get('/', function () use ($app) { $repositories = $app['git']->getRepositories($app['git.repos']); $lastCommit = []; foreach ($repositories as $repo) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo['name']); $command = 'log --graph --date-order --all -C -M -n 1 --date=rfc ' . '--pretty=format:"B[%D] C[%H] D[%ad] A[%an] E[%ae] H[%h] S[%s]"'; $rawRows = $repository->getClient()->run($repository, $command); $rawRows = explode("\n", $rawRows); foreach ($rawRows as $row) { if (preg_match("/^(.+?)(\s(B\[(.*?)\])? C\[(.+?)\] D\[(.+?)\] A\[(.+?)\] E\[(.+?)\] H\[(.+?)\] S\[(.+?)\])?$/", $row, $output)) { if (!isset($output[4])) { $graphItems[] = array( "relation" => $output[1] ); continue; } $branch = $output[4]; $branchArray = explode('->', $branch); if (isset($branchArray[1])) { $branch = trim($branchArray[1]); } $repositories[$repo['name']]['time'] = $output[6]; $repositories[$repo['name']]['timestamp'] = strtotime($output[6]); $repositories[$repo['name']]['user'] = $output[7]; $repositories[$repo['name']]['branch'] = $branch; $repositories[$repo['name']]['key'] = $repo['name']; /* $graphItems[] = array( "relation"=>$output[1], "branch"=>$output[4], "rev"=>$output[5], "date"=>$output[6], "author"=>$output[7], "author_email"=>$output[8], "short_rev"=>$output[9], "subject"=>preg_replace('/(^|\s)(#[[:xdigit:]]+)(\s|$)/', '$1$2$3', $output[10]) ); */ } } } uksort($repositories, function($a, $b) use ($repositories) { $timestampA = isset($repositories[$a]['timestamp']) ? $repositories[$a]['timestamp'] : -1; $timestampB = isset($repositories[$b]['timestamp']) ? $repositories[$b]['timestamp'] : -1; return $timestampA < $timestampB ? 1 : -1; }); return $app['twig']->render('index.twig', array( 'repositories' => $repositories, 'branch' => '', 'repo' => '', )); })->bind('homepage'); $route->get('/refresh', function (Request $request) use ($app) { # Go back to calling page return $app->redirect($request->headers->get('Referer')); })->bind('refresh'); $route->get('{repo}/stats/{branch}', function ($repo, $branch) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($branch === null) { $branch = $repository->getHead(); } $stats = $repository->getStatistics($branch); $authors = $repository->getAuthorStatistics($branch); return $app['twig']->render('stats.twig', array( 'repo' => $repo, 'branch' => $branch, 'branches' => $repository->getBranches(), 'browse_type' => 'stats', 'tags' => $repository->getTags(), 'stats' => $stats, 'authors' => $authors, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('branch', $app['util.routing']->getBranchRegex()) ->value('branch', null) ->convert('branch', 'escaper.argument:escape') ->bind('stats'); $route->get('{repo}/{branch}/rss/', function ($repo, $branch) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($branch === null) { $branch = $repository->getHead(); } $commits = $repository->getPaginatedCommits($branch); $html = $app['twig']->render('rss.twig', array( 'repo' => $repo, 'branch' => $branch, 'commits' => $commits, )); return new Response($html, 200, array('Content-Type' => 'application/rss+xml')); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('branch', $app['util.routing']->getBranchRegex()) ->value('branch', null) ->convert('branch', 'escaper.argument:escape') ->bind('rss'); return $route; } } src/GitList/Controller/NetworkController.php000066400000000000000000000120711516067322700215670ustar00rootroot00000000000000get('{repo}/network/{commitishPath}/{page}.json', function ($repo, $commitishPath, $page) use ($app) { /** @var $repository Repository */ $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($commitishPath === null) { $commitishPath = $repository->getHead(); } $pager = $app['util.view']->getPager($page, $repository->getTotalCommits($commitishPath)); $commits = $repository->getPaginatedCommits($commitishPath, $pager['current']); $jsonFormattedCommits = array(); foreach ($commits as $commit) { $detailsUrl = $app['url_generator']->generate( 'commit', array( 'repo' => $repo, 'commit' => $commit->getHash() ) ); $jsonFormattedCommits[$commit->getHash()] = array( 'hash' => $commit->getHash(), 'parentsHash' => $commit->getParentsHash(), 'date' => $commit->getDate()->format('U'), 'message' => htmlentities($commit->getMessage()), 'details' => $detailsUrl, 'author' => array( 'name' => $commit->getAuthor()->getName(), 'email' => $commit->getAuthor()->getEmail(), 'image' => $app->getAvatar($commit->getAuthor()->getEmail(), 40) ) ); } $nextPageUrl = null; if ($pager['last'] !== $pager['current']) { $nextPageUrl = $app['url_generator']->generate( 'networkData', array( 'repo' => $repo, 'commitishPath' => $commitishPath, 'page' => $pager['next'] ) ); } // when no commits are given, return an empty response - issue #369 if (count($commits) === 0) { return $app->json( array( 'repo' => $repo, 'commitishPath' => $commitishPath, 'nextPage' => null, 'start' => null, 'commits' => $jsonFormattedCommits ), 200 ); } return $app->json( array( 'repo' => $repo, 'commitishPath' => $commitishPath, 'nextPage' => $nextPageUrl, 'start' => $commits[0]->getHash(), 'commits' => $jsonFormattedCommits ), 200 ); } )->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->value('commitishPath', null) ->convert('commitishPath', 'escaper.argument:escape') ->assert('page', '\d+') ->value('page', '0') ->bind('networkData'); $route->get( '{repo}/network/{commitishPath}', function ($repo, $commitishPath) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($commitishPath === null) { $commitishPath = $repository->getHead(); } list($branch, $file) = $app['util.routing']->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); return $app['twig']->render( 'network.twig', array( 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), 'browse_type' => 'network', 'repo' => $repo, 'branch' => $branch, 'commitishPath' => $commitishPath, ) ); } )->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->value('commitishPath', null) ->convert('commitishPath', 'escaper.argument:escape') ->bind('network'); return $route; } } src/GitList/Controller/TreeController.php000066400000000000000000000141241516067322700210360ustar00rootroot00000000000000get('{repo}/tree/{commitishPath}/', $treeController = function ($repo, $commitishPath = '') use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $head = $repository->getHead(); if (!$commitishPath) { $commitishPath = $head; } list($branch, $tree) = $app['util.routing']->parseCommitishPathParam($commitishPath, $repo); list($branch, $tree) = $app['util.repository']->extractRef($repository, $branch, $tree); $files = $repository->getTree($tree ? "$branch:\"$tree\"/" : $branch); $breadcrumbs = $app['util.view']->getBreadcrumbs($tree); $parent = null; if (($slash = strrpos($tree, '/')) !== false) { $parent = substr($tree, 0, $slash); } elseif (!empty($tree)) { $parent = ''; } return $app['twig']->render('tree.twig', array( 'head' => $head, 'files' => $files->output(), 'repo' => $repo, 'branch' => $branch, 'path' => $tree ? $tree . '/' : $tree, 'parent' => $parent, 'breadcrumbs' => $breadcrumbs, 'branches' => $repository->getBranches(), 'browse_type' => 'tree', 'tags' => $repository->getTags(), 'readme' => $app['util.repository']->getReadme($repository, $branch, $tree ? "$tree" : ""), )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->convert('commitishPath', 'escaper.argument:escape') ->bind('tree'); $route->post('{repo}/tree/{branch}/search', function (Request $request, $repo, $branch = '', $tree = '') use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if (!$branch) { $branch = $repository->getHead(); } $query = $request->get('query'); $breadcrumbs = array(array('dir' => 'Search results for: ' . $query, 'path' => '')); $results = $repository->searchTree($query, $branch); if ($results === false) { $results = []; } for($i = 0; $i < count($results); $i++) { $result = $results[$i]; $results[$i]['type'] = $app['util.repository']->getFileType($result['file']); } return $app['twig']->render('search.twig', array( 'results' => $results, 'repo' => $repo, 'path' => $tree, 'breadcrumbs' => $breadcrumbs, 'branch' => $branch, 'branches' => $repository->getBranches(), 'browse_type' => 'search', 'tags' => $repository->getTags(), 'query' => $query )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('branch', $app['util.routing']->getBranchRegex()) ->convert('branch', 'escaper.argument:escape') ->bind('search'); $route->get('{repo}/{format}ball/{branch}', function($repo, $format, $branch) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $tree = $repository->getBranchTree($branch); if (false === $tree) { return $app->abort(404, 'Invalid commit or tree reference: ' . $branch); } $file = $app['cache.archives'] . DIRECTORY_SEPARATOR . $repo . DIRECTORY_SEPARATOR . substr($tree, 0, 2) . DIRECTORY_SEPARATOR . substr($tree, 2) . '.' . $format; if (!file_exists($file)) { $repository->createArchive($tree, $file, $format); } /** * Generating name for downloading, lowercasing and removing all non * ascii and special characters */ $filename = strtolower($repo.'_'.$branch); $filename = preg_replace('#[^a-z0-9]+#', '_', $filename); $filename = $filename . '.' . $format; $response = new BinaryFileResponse($file); $response->setContentDisposition('attachment', $filename); return $response; })->assert('format', '(zip|tar)') ->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('branch', $app['util.routing']->getBranchRegex()) ->convert('branch', 'escaper.argument:escape') ->bind('archive'); // this is weird ... was / , not working, i changed to \/ , now it works $route->get('{repo}\/{branch}', function($repo, $branch) use ($app) { return $app->redirect( $app['url_subdir'] . '/' . $repo . '/tree/' . $branch); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('branch', $app['util.routing']->getBranchRegex()) ->convert('branch', 'escaper.argument:escape') ->bind('branch'); $route->get('{repo}/', function($repo) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $head = $repository->getHead(); return $app->redirect( $app['url_subdir'] . '/' . $repo . '/tree/' . $head); })->assert('repo', $app['util.routing']->getRepositoryRegex()) ->bind('repository'); return $route; } } src/GitList/Controller/TreeGraphController.php000066400000000000000000000061251516067322700220220ustar00rootroot00000000000000get( '{repo}/treegraph/{commitishPath}', function ($repo, $commitishPath) use ($app) { $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $command = 'log --graph --date-order --all -C -M -n 100 --date=iso ' . '--pretty=format:"B[%d] C[%H] D[%ad] A[%an] E[%ae] H[%h] S[%s]"'; $rawRows = $repository->getClient()->run($repository, $command); $rawRows = explode("\n", $rawRows); $graphItems = array(); foreach ($rawRows as $row) { if (preg_match("/^(.+?)(\s(B\[(.*?)\])? C\[(.+?)\] D\[(.+?)\] A\[(.+?)\] E\[(.+?)\] H\[(.+?)\] S\[(.+?)\])?$/", $row, $output)) { if (!isset($output[4])) { $graphItems[] = array( "relation"=>$output[1] ); continue; } $graphItems[] = array( "relation"=>$output[1], "branch"=>$output[4], "rev"=>$output[5], "date"=>$output[6], "author"=>$output[7], "author_email"=>$output[8], "short_rev"=>$output[9], "subject"=>preg_replace('/(^|\s)(#[[:xdigit:]]+)(\s|$)/', '$1$2$3', $output[10]) ); } } if ($commitishPath === null) { $commitishPath = $repository->getHead(); } list($branch, $file) = $app['util.routing']->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); return $app['twig']->render( 'treegraph.twig', array( 'repo' => $repo, 'branch' => $branch, 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), 'browse_type' => 'treegraph', 'commitishPath' => $commitishPath, 'graphItems' => $graphItems, ) ); } )->assert('repo', $app['util.routing']->getRepositoryRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->value('commitishPath', null) ->convert('commitishPath', 'escaper.argument:escape') ->bind('treegraph'); return $route; } } src/GitList/Escaper/000077500000000000000000000000001516067322700146175ustar00rootroot00000000000000src/GitList/Escaper/ArgumentEscaper.php000066400000000000000000000003371516067322700204200ustar00rootroot00000000000000factory(function () use ($app) { return new Repository($app); }); } public function boot(Container $app) { } } src/GitList/Provider/RoutingUtilServiceProvider.php000066400000000000000000000010751516067322700230640ustar00rootroot00000000000000factory(function () use ($app) { return new Routing($app); }); } public function boot(Container $app) { } } src/GitList/Provider/ViewUtilServiceProvider.php000066400000000000000000000010411516067322700223400ustar00rootroot00000000000000factory(function () { return new View; }); } public function boot(Container $app) { } } src/GitList/Util/000077500000000000000000000000001516067322700141525ustar00rootroot00000000000000src/GitList/Util/Repository.php000066400000000000000000000154751516067322700170560ustar00rootroot00000000000000 'php', 'c' => 'clike', 'h' => 'clike', 'cpp' => 'clike', 'm' => 'clike', 'mm' => 'clike', 'ino' => 'clike', 'cs' => 'text/x-csharp', 'java' => 'text/x-java', 'clj' => 'clojure', 'coffee' => 'coffeescript', 'css' => 'css', 'diff' => 'diff', 'ecl' => 'ecl', 'el' => 'erlang', 'go' => 'go', 'groovy' => 'groovy', 'hbs' => 'handlebars', 'hs' => 'haskell', 'lhs' => 'haskell', 'jsp' => 'application/x-jsp', 'asp' => 'htmlembedded', 'aspx' => 'htmlembedded', 'html' => 'htmlmixed', 'tpl' => 'htmlmixed', 'js' => 'javascript', 'json' => 'javascript', 'jsx' => 'jsx', 'less' => 'css', 'lua' => 'lua', 'md' => 'markdown', 'markdown' => 'markdown', 'sql' => 'sql', 'swift' => 'swift', 'twig' => 'twig', 'ml' => 'ocaml', 'mli' => 'ocaml', 'pl' => 'perl', 'pm' => 'perl', 'pas' => 'pascal', 'ps1' => 'powershell', 'ini' => 'properties', 'cfg' => 'properties', 'nt' => 'ntriples', 'py' => 'python', 'rb' => 'ruby', 'rst' => 'rst', 'r' => 'r', 'lock' => 'javascript', 'sh' => 'shell', 'ss' => 'scheme', 'scala' => 'text/x-scala', 'scm' => 'scheme', 'sls' => 'scheme', 'sps' => 'scheme', 'rs' => 'rust', 'st' => 'smalltalk', 'tex' => 'stex', 'vbs' => 'vbscript', 'vb' => 'vbscript', 'v' => 'verilog', 'xml' => 'xml', 'xsd' => 'xml', 'xsl' => 'xml', 'xul' => 'xml', 'xlf' => 'xml', 'xliff' => 'xml', 'xaml' => 'xml', 'wxs' => 'xml', 'wxl' => 'xml', 'wxi' => 'xml', 'wsdl' => 'xml', 'svg' => 'xml', 'rss' => 'xml', 'rdf' => 'xml', 'plist' => 'xml', 'mxml' => 'xml', 'kml' => 'xml', 'glade' => 'xml', 'xq' => 'xquery', 'xqm' => 'xquery', 'xquery' => 'xquery', 'xqy' => 'xquery', 'yml' => 'yaml', 'yaml' => 'yaml', 'png' => 'image', 'jpg' => 'image', 'gif' => 'image', 'jpeg' => 'image', 'bmp' => 'image', 'csproj' => 'xml', 'iml' => 'xml', ]; protected static $binaryTypes = [ 'exe', 'com', 'so', 'la', 'o', 'dll', 'pyc', 'jpg', 'jpeg', 'bmp', 'gif', 'png', 'xmp', 'pcx', 'svgz', 'ttf', 'tiff', 'oet', 'gz', 'tar', 'rar', 'zip', '7z', 'jar', 'class', 'odt', 'ods', 'pdf', 'doc', 'docx', 'dot', 'xls', 'xlsx', 'gzip' ]; public function __construct(Application $app) { $this->app = $app; } /** * Returns the file type based on filename by treating the extension. * * The file type is used by CodeMirror, a Javascript-based IDE implemented in * GitList, to properly highlight the blob syntax (if it's a source-code) * * @param string $file File name * * @return mixed File type */ public function getFileType($file) { if ($file === 'Dockerfile') { return 'dockerfile'; }; if (($pos = strrpos($file, '.')) !== false) { $fileType = strtolower(substr($file, $pos + 1)); } else { return 'text'; } if (!empty($this->app['filetypes'])) { if (isset($this->app['filetypes'][$fileType])) { return $this->app['filetypes'][$fileType]; } } if (isset($this->defaultFileTypes[$fileType])) { return $this->defaultFileTypes[$fileType]; } return 'text'; } /** * Returns whether the file is binary. * * @param string $file * * @return bool */ public function isBinary($file) { if (($pos = strrpos($file, '.')) !== false) { $fileType = substr($file, $pos + 1); } else { return false; } if (!empty($this->app['binary_filetypes']) && array_key_exists($fileType, $this->app['binary_filetypes'])) { return $this->app['binary_filetypes'][$fileType]; } if (in_array($fileType, self::$binaryTypes)) { return true; } return false; } public function getReadme($repository, $branch = null, $path = '') { if ($branch === null) { $branch = $repository->getHead(); } if ($path != '') { $path = "$path/"; } $files = $repository->getTree($path != '' ? "$branch:\"$path\"" : $branch)->output(); foreach ($files as $file) { if (preg_match('/^readme*/i', $file['name'])) { return [ 'filename' => $file['name'], 'content' => $repository->getBlob("$branch:\"$path{$file['name']}\"")->output(), ]; } } // No contextual readme, try to catch the main one if we are in deeper context if ($path != '') { return $this->getReadme($repository, $branch, ''); } return []; } /** * Returns an Array where the first value is the tree-ish and the second is the path. * * @param \GitList\Git\Repository $repository * @param string $branch * @param string $tree * * @return array */ public function extractRef($repository, $branch = '', $tree = '') { $branch = trim($branch, '/'); $tree = trim($tree, '/'); $input = $branch . '/' . $tree; // If the ref appears to be a SHA, just split the string if (preg_match('/^([[:alnum:]]{40})(.+)/', $input, $matches)) { $branch = $matches[1]; } else { // Otherwise, attempt to detect the ref using a list of the project's branches and tags $validRefs = array_merge((array) $repository->getBranches(), (array) $repository->getTags()); foreach ($validRefs as $key => $ref) { if (!preg_match(sprintf('#^%s/#', preg_quote($ref, '#')), $input)) { unset($validRefs[$key]); } } // No exact ref match, so just try our best if (count($validRefs) > 1) { preg_match('/([^\/]+)(.*)/', $input, $matches); $branch = preg_replace('/^\/|\/$/', '', $matches[1]); } else { // Extract branch name $branch = array_shift($validRefs); } } return [$branch, $tree]; } }src/GitList/Util/Routing.php000066400000000000000000000114761516067322700163230ustar00rootroot00000000000000app = $app; } /* @brief Return $commitish, $path parsed from $commitishPath, based on * what's in $repo. Raise a 404 if $branchpath does not represent a * valid branch and path. * * A helper for parsing routes that use commit-ish names and paths * separated by /, since route regexes are not enough to get that right. * * @param string $commitishPath * @param string $repo * @return array */ public function parseCommitishPathParam($commitishPath, $repo) { $app = $this->app; $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $commitish = null; $path = null; $slashPosition = strpos($commitishPath, '/'); if (strlen($commitishPath) >= 40 && ($slashPosition === false || $slashPosition === 40)) { // We may have a commit hash as our commitish. $hash = substr($commitishPath, 0, 40); if (preg_match('/[^a-zA-Z0-9]/i', $hash) === 0) { if ($repository->hasCommit($hash)) { $commitish = $hash; } } } if ($commitish === null) { $branches = $repository->getBranches(); $tags = $repository->getTags(); if ($tags !== null && count($tags) > 0) { $branches = array_merge($branches, $tags); } $matchedBranch = null; $matchedBranchLength = 0; foreach ($branches as $branch) { if (strpos($commitishPath, $branch) === 0 && strlen($branch) > $matchedBranchLength) { $matchedBranch = $branch; $matchedBranchLength = strlen($matchedBranch); } } if ($matchedBranch !== null) { $commitish = $matchedBranch; } else { // We may have partial commit hash as our commitish. $hash = $slashPosition === false ? $commitishPath : substr($commitishPath, 0, $slashPosition); if ($repository->hasCommit($hash)) { $commit = $repository->getCommit($hash); $commitish = $commit->getHash(); } else { throw new EmptyRepositoryException('This repository is currently empty. There are no commits.'); } } } $commitishLength = strlen($commitish); $path = substr($commitishPath, $commitishLength); if (strpos($path, '/') === 0) { $path = substr($path, 1); } return array($commitish, $path); } public function getBranchRegex() { static $branchRegex = null; if ($branchRegex === null) { $branchRegex = '(?!/|.*([/.]\.|//|@\{|\\\\))[^\040\177 ~^:?*\[]+(?app['git']->getRepositories($this->app['git.repos']) ); usort( $quotedPaths, function ($a, $b) { return strlen($b) - strlen($a); } ); $regex = implode('|', $quotedPaths); } return $regex; } public function isWindows() { switch (PHP_OS) { case 'WIN32': case 'WINNT': case 'Windows': return true; default: return false; } } /** * Strips the base path from a full repository path * * @param string $repoPath Full path to the repository * @return string Relative path to the repository from git.repositories */ public function getRelativePath($repoPath) { if (strpos($repoPath, $this->app['git.repos']) === 0) { $relativePath = substr($repoPath, strlen($this->app['git.repos'])); return ltrim(strtr($relativePath, '\\', '/'), '/'); } else { throw new \InvalidArgumentException( sprintf("Path '%s' does not match configured repository directory", $repoPath) ); } } } src/GitList/Util/View.php000066400000000000000000000024071516067322700156000ustar00rootroot00000000000000 $path) { $breadcrumbs[] = array( 'dir' => $path, 'path' => implode('/', array_slice($paths, 0, $i + 1)), ); } return $breadcrumbs; } public function getPager($pageNumber, $totalCommits) { $pageNumber = (empty($pageNumber)) ? 0 : $pageNumber; $lastPage = intval($totalCommits / 15); // If total commits are integral multiple of 15, the lastPage will be commits/15 - 1. $lastPage = ($lastPage * 15 == $totalCommits) ? $lastPage - 1 : $lastPage; $nextPage = $pageNumber + 1; $previousPage = $pageNumber - 1; return array('current' => $pageNumber, 'next' => $nextPage, 'previous' => $previousPage, 'last' => $lastPage, 'total' => $totalCommits, ); } } src/Gitter/000077500000000000000000000000001516067322700131145ustar00rootroot00000000000000src/Gitter/Client.php000066400000000000000000000201541516067322700150450ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter; use Symfony\Component\Process\Process; use Symfony\Component\Process\ExecutableFinder; class Client { protected $defaultBranch; protected $hidden; protected $projects; protected $path; public function __construct($options = null) { $path = null; if (is_array($options)) { $this->setDefaultBranch($options['default_branch']); $this->setHidden($options['hidden']); $this->setProjects($options['projects'] ?? array()); $path = $options['path']; } if (!$path) { $finder = new ExecutableFinder(); $path = $finder->find('git', '/usr/bin/git'); } $this->setPath($path); } /** * Creates a new repository on the specified path * * @param string $path Path where the new repository will be created * @return Repository Instance of Repository */ public function createRepository($path, $bare = null) { if (file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { throw new \RuntimeException('A GIT repository already exists at ' . $path); } $repository = new Repository($path, $this); return $repository->create($bare); } /** * Opens a repository at the specified path * * @param string $path Path where the repository is located * @return Repository Instance of Repository */ public function getRepository($path) { if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { throw new \RuntimeException('There is no GIT repository at ' . $path); } return new Repository($path, $this); } public function run($repository, $command) { if (version_compare($this->getVersion(), '1.7.2', '>=')) { $command = '-c "color.ui"=false ' . $command; } $command = $this->getPath() . ' ' . $command; // echo $command; // echo "
"; // echo $repository->getPath(); // echo "
"; // echo "
"; $process = new Process($command, $repository->getPath()); $process->setTimeout(180); $process->enableOutput(); $process->run(); if (!$process->isSuccessful()) { throw new \RuntimeException($process->getExitCode() . " - " . $process->getExitCodeText(). " - " . $process->getErrorOutput() . " - " . $process->getOutput() ); } return $process->getOutput(); } public function getVersion() { static $version; if (null !== $version) { return $version; } $process = new Process($this->getPath() . ' --version'); $process->run(); if (!$process->isSuccessful()) { throw new \RuntimeException($process->getErrorOutput()); } $version = trim(substr($process->getOutput(), 12)); return $version; } /** * Get the current Git binary path * * @return string Path where the Git binary is located */ protected function getPath() { return escapeshellarg($this->path); } /** * Set the current Git binary path * * @param string $path Path where the Git binary is located */ protected function setPath($path) { $this->path = $path; return $this; } /** * Set default branch as a string. * * @param string $branch Name of branch to use when repo's HEAD is detached. * @return object */ protected function setDefaultBranch($branch) { $this->defaultBranch = $branch; return $this; } /** * Return name of default branch as a string. */ public function getDefaultBranch() { return $this->defaultBranch; } /** * Get hidden repository list * * @return array List of repositories to hide */ protected function getHidden() { return $this->hidden; } /** * Set the hidden repository list * * @param array $hidden List of repositories to hide * @return object */ protected function setHidden($hidden) { $this->hidden = $hidden; return $this; } /** * Get project list * * @return array List of repositories to show */ protected function getProjects() { return $this->projects; } /** * Set the shown repository list * * @param array $projects List of repositories to show */ protected function setProjects($projects) { $this->projects = $projects; return $this; } public function getRepositoryFromName($paths, $repo) { $repositories = $this->getRepositories($paths); $path = $repositories[$repo]['path']; return $this->getRepository($path); } /** * Searches for valid repositories on the specified path * * @param array $paths Array of paths where repositories will be searched * @return array Found repositories, containing their name, path and description sorted * by repository name */ public function getRepositories($paths) { $allRepositories = array(); foreach ($paths as $path) { $repositories = $this->recurseDirectory($path); if (empty($repositories)) { throw new \RuntimeException('There are no GIT repositories in ' . $path); } /** * Use "+" to preserve keys, only a problem with numeric repos */ $allRepositories = $allRepositories + $repositories; } $allRepositories = array_unique($allRepositories, SORT_REGULAR); uksort($allRepositories, function($k1, $k2) { return strtolower($k2)isDot()) { continue; } if (strrpos($file->getFilename(), '.') === 0) { continue; } if (!$file->isReadable()) { continue; } if ($file->isDir()) { $isBare = file_exists($file->getPathname() . '/HEAD'); $isRepository = file_exists($file->getPathname() . '/.git/HEAD'); if ($isRepository || $isBare) { if (in_array($file->getPathname(), $this->getHidden())) { continue; } if ($isBare) { $description = $file->getPathname() . '/description'; } else { $description = $file->getPathname() . '/.git/description'; } if (file_exists($description)) { $description = file_get_contents($description); } else { $description = null; } $repoName = $appendPath . $file->getFilename(); if (is_array($this->getProjects()) && !in_array($repoName, $this->getProjects())) { continue; } $repositories[$repoName] = array( 'name' => $repoName, 'path' => $file->getPathname(), 'description' => $description ); continue; } else { $repositories = array_merge($repositories, $this->recurseDirectory($file->getPathname(), $appendPath . $file->getFilename() . '/')); } } } return $repositories; } } src/Gitter/Model/000077500000000000000000000000001516067322700141545ustar00rootroot00000000000000src/Gitter/Model/AbstractModel.php000066400000000000000000000011021516067322700174030ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; use Gitter\Repository; abstract class AbstractModel { protected $repository; public function getRepository() : Repository { return $this->repository; } public function setRepository(Repository $repository) { $this->repository = $repository; return $this; } } src/Gitter/Model/Blob.php000066400000000000000000000012761516067322700155510ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; use Gitter\Repository; class Blob extends Item { public function __construct($hash, Repository $repository) { $this->setHash($hash); $this->setRepository($repository); } public function output() { $data = $this->getRepository()->getClient()->run($this->getRepository(), 'show ' . $this->getHash()); return $data; } public function isBlob() { return true; } } src/Gitter/Model/Branch.php000066400000000000000000000007561516067322700160720ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; class Branch extends AbstractModel { protected $name; public function getName() { return $this->name; } public function setName($name) { $this->name = $name; return $this; } } src/Gitter/Model/Commit/000077500000000000000000000000001516067322700154045ustar00rootroot00000000000000src/Gitter/Model/Commit/Author.php000066400000000000000000000014531516067322700173620ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model\Commit; use Gitter\Model\AbstractModel; class Author extends AbstractModel { protected $name; protected $email; public function __construct($name, $email) { $this->setName($name); $this->setEmail($email); } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getEmail() { return $this->email; } public function setEmail($email) { $this->email = $email; } } src/Gitter/Model/Commit/Commit.php000066400000000000000000000067111516067322700173520ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model\Commit; use Gitter\Model\Item; use Gitter\Model\Diff; use Gitter\Util\DateTime; class Commit extends Item { protected $shortHash; protected $treeHash; protected $parentsHash; protected $author; protected $date; protected $commiter; protected $commiterDate; protected $message; protected $body; /** @var Diff[] */ protected $diffs; public function importData(array $data) { $this->setHash($data['hash']); $this->setShortHash($data['short_hash']); $this->setTreeHash($data['tree']); $this->setParentsHash(isset($data['parents']) ? array_filter(explode(' ', $data['parents'])) : array()); $this->setAuthor( new Author($data['author'], $data['author_email']) ); $this->setDate( new DateTime('@' . $data['date']) ); $this->setCommiter( new Author($data['commiter'], $data['commiter_email']) ); $this->setCommiterDate( new DateTime('@' . $data['commiter_date']) ); $this->setMessage($data['message']); if (isset($data['body'])) { $this->setBody($data['body']); } } public function getShortHash() { return $this->shortHash; } public function setShortHash($shortHash) { $this->shortHash = $shortHash; return $this; } public function getTreeHash() { return $this->treeHash; } public function setTreeHash($treeHash) { $this->treeHash = $treeHash; return $this; } public function getParentsHash() { return $this->parentsHash; } public function setParentsHash($parentsHash) { $this->parentsHash = $parentsHash; return $this; } public function getAuthor() { return $this->author; } public function setAuthor($author) { $this->author = $author; return $this; } public function getDate() { return $this->date; } public function setDate($date) { $this->date = $date; return $this; } public function getCommiter() { return $this->commiter; } public function setCommiter($commiter) { $this->commiter = $commiter; return $this; } public function getCommiterDate() { return $this->commiterDate; } public function setCommiterDate($commiterDate) { $this->commiterDate = $commiterDate; return $this; } public function getMessage() { return $this->message; } public function setMessage($message) { $this->message = $message; return $this; } public function getBody() { return $this->body; } public function setBody($body) { $this->body = $body; return $this; } public function getDiffs() { return $this->diffs; } public function setDiffs($diffs) { $this->diffs = $diffs; return $this; } public function getChangedFiles() { return sizeof($this->diffs); } public function isCommit() { return true; } } src/Gitter/Model/Commit/Diff.php000066400000000000000000000026671516067322700170000ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model\Commit; use Gitter\Model\AbstractModel; class Diff extends AbstractModel { /** @var DiffLine[] */ protected $lines; protected $index; protected $old; protected $new; protected $file; protected $binary = false; public $lineCount = 0; public function addLine($line, $oldNo, $newNo) { $this->lines[] = new DiffLine($line, $oldNo, $newNo); } public function getLines() { return $this->lines; } public function setIndex($index) { $this->index = $index; } public function getIndex() { return $this->index; } public function setOld($old) { $this->old = $old; } public function getOld() { return $this->old; } public function setNew($new) { $this->new = $new; } public function getNew() { return $this->new; } public function setFile($file) { $this->file = $file; } public function getFile() { return $this->file; } public function setBinary($bool) { $this->binary = true; } public function getBinary() { return $this->binary; } } src/Gitter/Model/Commit/DiffLine.php000066400000000000000000000027251516067322700176030ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model\Commit; use Gitter\Model\Line; class DiffLine extends Line { protected $numNew; protected $numOld; public function __construct($data, $numOld, $numNew) { parent::__construct($data); if (!empty($data)) { switch ($data[0]) { case '@': $this->numOld = '...'; $this->numNew = '...'; break; case '-': $this->numOld = $numOld; $this->numNew = ''; break; case '+': $this->numOld = ''; $this->numNew = $numNew; break; default: $this->numOld = $numOld; $this->numNew = $numNew; } } else { $this->numOld = $numOld; $this->numNew = $numNew; } } public function getNumOld() { return $this->numOld; } public function setNumOld($num) { $this->numOld = $num; } public function getNumNew() { return $this->numNew; } public function setNumNew($num) { $this->numNew = $num; } } src/Gitter/Model/File.php000066400000000000000000000023401516067322700155430ustar00rootroot00000000000000size; } public function setSize($size) { $this->size = $size; return $this; } public function getPath() { return $this->path; } public function setPath($path) { $this->path = $path; return $this; } public function getMode() { return $this->mode; } public function setMode($mode) { $this->mode = $mode; return $this; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; return $this; } public function getHash() { return $this->hash; } public function setHash($hash) { $this->hash = $hash; return $this; } public function getShortHash() { return $this->shortHash; } public function setShortHash($hash) { $this->shortHash = $hash; return $this; } }src/Gitter/Model/Item.php000066400000000000000000000010461516067322700155640ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; class Item extends File { public function isBlob() { return false; } public function isTag() { return false; } public function isCommit() { return false; } public function isTree() { return false; } } src/Gitter/Model/Line.php000066400000000000000000000020631516067322700155550ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; class Line extends AbstractModel { protected $line; protected $type; public function __construct($data) { if (!empty($data)) { if ($data[0] == '@') { $this->setType('chunk'); } if ($data[0] == '-') { $this->setType('old'); } if ($data[0] == '+') { $this->setType('new'); } } $this->setLine($data); } public function getLine() { return $this->line; } public function setLine($line) { $this->line = $line; return $this; } public function getType() { return $this->type; } public function setType($type) { $this->type = $type; return $this; } } src/Gitter/Model/Module.php000066400000000000000000000003271516067322700161140ustar00rootroot00000000000000url = $url; } public function getUrl() { return $this->url; } } src/Gitter/Model/Symlink.php000066400000000000000000000004471516067322700163200ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; class Symlink extends File { } src/Gitter/Model/Tag.php000066400000000000000000000010401516067322700153730ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; class Tag extends Item { protected $name; public function getName() { return $this->name; } public function setName($name) { $this->name = $name; return $this; } public function isTag() { return true; } } src/Gitter/Model/Tree.php000066400000000000000000000154161516067322700155730ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Model; use Gitter\Repository; use function Stringy\create; use Stringy\Stringy as S; class Tree extends Item implements \RecursiveIterator { protected $data; protected $position = 0; private $submodules = null; public function __construct($hash, Repository $repository) { $this->setHash($hash); $this->setRepository($repository); } private function getSubmodules($files, $hash) { if ($this->submodules === null) { foreach ($files as $file) { if ($file[4] === '.gitmodules') { $branch = $hash; $gitsubmodule = $this->getRepository()->getBlob("$branch:\"$file[4]\"")->output(); $this->submodules = parse_ini_string($gitsubmodule, true); } } if ($this->submodules === null && strpos($hash, ':') !== false) { // Search in root folder $data = $this->getRepository()->getClient()->run($this->getRepository(), 'ls-tree -lz ' . explode(':', $hash)[0]); $lines = explode("\0", $data); $rootFolderFiles = array(); $root = array(); foreach ($lines as $key => $line) { if (empty($line)) { unset($lines[$key]); continue; } $rootFolderFiles[] = preg_split("/[\s]+/", $line, 5); } $this->submodules = $this->getSubmodules($rootFolderFiles, explode(':', $hash)[0]); } } return $this->submodules; } public function parse() { $data = $this->getRepository()->getClient()->run($this->getRepository(), 'ls-tree -lz ' . $this->getHash()); $lines = explode("\0", $data); $files = array(); $root = array(); foreach ($lines as $key => $line) { if (empty($line)) { unset($lines[$key]); continue; } $files[] = preg_split("/[\s]+/", $line, 5); } foreach ($files as $file) { // submodule if ($file[0] == '160000') { $submodules = $this->getSubmodules($files, $this->getHash()); if (strpos($this->getHash(), ':') === false) { $submoduleName = $file[4]; } else { $submoduleName = str_replace('"', '', explode(':', $this->getHash())[1]) . "$file[4]"; } $shortHash = $this->getRepository()->getShortHash($file[2]); $tree = new Module; $tree->setMode($file[0]); $tree->setName($file[4]); $tree->setHash($file[2]); $tree->setShortHash($shortHash); $url = $submodules["submodule $submoduleName"]['url']; if (preg_match('/^https?:\/\/(www\.)?github.com\//i', $url)) { $s = S::create($url); if ($s->endsWith('.git')) { $url = substr($url, 0, strlen($url) - 4); } } $tree->setUrl($url); $root[] = $tree; continue; } if ($file[0] == '120000') { $show = $this->getRepository()->getClient()->run($this->getRepository(), 'show ' . $file[2]); $tree = new Symlink; $tree->setMode($file[0]); $tree->setName($file[4]); $tree->setPath($show); $root[] = $tree; continue; } if ($file[1] == 'blob') { $blob = new Blob($file[2], $this->getRepository()); $blob->setMode($file[0]); $blob->setName($file[4]); $blob->setSize($file[3]); $root[] = $blob; continue; } $tree = new Tree($file[2], $this->getRepository()); $tree->setMode($file[0]); $tree->setName($file[4]); $root[] = $tree; } $this->data = $root; } public function output() { $files = $folders = array(); foreach ($this as $node) { if ($node instanceof Blob) { $file['type'] = 'blob'; $file['name'] = $node->getName(); $file['size'] = $node->getSize(); $file['mode'] = $node->getMode(); $file['hash'] = $node->getHash(); $files[] = $file; continue; } if ($node instanceof Tree) { $folder['type'] = 'folder'; $folder['name'] = $node->getName(); $folder['size'] = ''; $folder['mode'] = $node->getMode(); $folder['hash'] = $node->getHash(); $folders[] = $folder; continue; } if ($node instanceof Module) { $folder['type'] = 'module'; $folder['name'] = $node->getName(); $folder['size'] = ''; $folder['mode'] = $node->getMode(); $folder['hash'] = $node->getHash(); $folder['shortHash'] = $node->getShortHash(); $folder['url'] = $node->getUrl(); $folders[] = $folder; continue; } if ($node instanceof Symlink) { $folder['type'] = 'symlink'; $folder['name'] = $node->getName(); $folder['size'] = ''; $folder['mode'] = $node->getMode(); $folder['hash'] = ''; $folder['path'] = $node->getPath(); $folders[] = $folder; } } // Little hack to make folders appear before files $files = array_merge($folders, $files); return $files; } public function valid() { return isset($this->data[$this->position]); } public function hasChildren() { return is_array($this->data[$this->position]); } public function next() { $this->position++; } public function current() { return $this->data[$this->position]; } public function getChildren() { return $this->data[$this->position]; } public function rewind() { $this->position = 0; } public function key() { return $this->position; } public function isTree() { return true; } }src/Gitter/PrettyFormat.php000066400000000000000000000015671516067322700162760ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter; class PrettyFormat { public function parse($output) { if (empty($output)) { throw new \RuntimeException('No data available'); } $data = $this->iteratorToArray(new \SimpleXmlIterator("$output")); return $data['item']; } protected function iteratorToArray($iterator) { foreach ($iterator as $key => $item) { if ($iterator->hasChildren()) { $data[$key][] = $this->iteratorToArray($item); continue; } $data[$key] = trim(strval($item)); } return $data; } } src/Gitter/Repository.php000066400000000000000000001200111516067322700157770ustar00rootroot00000000000000 * (c) Patrik Laszlo * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter; use Gitter\Model\Commit\Commit; use Gitter\Model\Tree; use Gitter\Model\Blob; use Gitter\Model\Commit\Diff; use Gitter\Statistics\StatisticsInterface; //use Gitter\PrettyFormat; use Symfony\Component\Filesystem\Filesystem; use Eloquent\Pathogen\FileSystem\FileSystemPath; use Spatie\TemporaryDirectory\TemporaryDirectory; class Repository { protected $path; protected $client; protected $commitsHaveBeenParsed = false; protected $statistics = array(); public function __construct($path, Client $client) { $this->setPath($path); $this->setClient($client); } /** * @param bool $value * @return void */ public function setCommitsHaveBeenParsed($value) { $this->commitsHaveBeenParsed = $value; } /** * @return boolean */ public function getCommitsHaveBeenParsed() { return $this->commitsHaveBeenParsed; } /** * Create a new git repository */ public function create($bare = null) { mkdir($this->getPath()); $command = 'init'; if ($bare) { $command .= ' --bare'; } $this->getClient()->run($this, $command); return $this; } /** * Get a git configuration variable * * @param string $key Configuration key */ public function getConfig($key) { $key = $this->getClient()->run($this, 'config ' . $key); return trim($key); } /** * Set a git configuration variable * * @param string $key Configuration key * @param string $value Configuration value */ public function setConfig($key, $value) { $this->getClient()->run($this, "config $key \"$value\""); return $this; } /** * Add statistic aggregator * * @param StatisticsInterface|array $statistics */ public function addStatistics ($statistics) { if (!is_array($statistics)) { $statistics = array($statistics); } foreach ($statistics as $statistic) { $reflect = new \ReflectionClass($statistic); $this->statistics[strtolower($reflect->getShortName())] = $statistic; } } /** * Get statistic aggregators * * @return array */ public function getStatistics($branch = null) { // Calculate amount of files, extensions and file size $logs = $this->getClient()->run($this, 'ls-tree -r -l ' . $branch); $lines = explode("\n", $logs); $files = []; $data['extensions'] = []; $data['size'] = 0; $data['files'] = 0; foreach ($lines as $key => $line) { if (empty($line)) { unset($lines[$key]); continue; } $files[] = preg_split("/[\s]+/", $line); } foreach ($files as $file) { if ($file[1] == 'blob') { $data['files']++; } if (is_numeric($file[3])) { $data['size'] += $file[3]; } } $logs = $this->getClient()->run($this, 'ls-tree -l -r --name-only ' . $branch); $files = explode("\n", $logs); foreach ($files as $file) { if (($pos = strrpos($file, '.')) !== false) { $extension = substr($file, $pos); if (($pos = strrpos($extension, '/')) === false) { $data['extensions'][] = $extension; } } } $data['extensions'] = array_count_values($data['extensions']); arsort($data['extensions']); return $data; } /** * Add untracked files * * @param mixed $files Files to be added to the repository */ public function add($files = '.') { if (is_array($files)) { $files = implode(' ', array_map('escapeshellarg', $files)); } else { $files = escapeshellarg($files); } $this->getClient()->run($this, "add $files"); return $this; } /** * Add all untracked files */ public function addAll() { $this->getClient()->run($this, "add -A"); return $this; } /** * Commit changes to the repository * * @param string $message Description of the changes made */ public function commit($message) { $this->getClient()->run($this, "commit -m \"$message\""); return $this; } /** * Checkout a branch * * @param string $branch Branch to be checked out */ public function checkout($branch) { $this->getClient()->run($this, "checkout $branch"); return $this; } /** * Pull repository changes */ public function pull() { $this->getClient()->run($this, "pull"); return $this; } /** * Update remote references * * @param string $repository Repository to be pushed * @param string $refspec Refspec for the push */ public function push($repository = null, $refspec = null) { $command = "push"; if ($repository) { $command .= " $repository"; } if ($refspec) { $command .= " $refspec"; } $this->getClient()->run($this, $command); return $this; } /** * Get name of repository (top level directory) * * @return string */ public function getName () { $name = rtrim($this->path, '/'); if (strstr($name, DIRECTORY_SEPARATOR)) { $name = substr($name, strrpos($name, DIRECTORY_SEPARATOR) + 1); } return trim($name); } /** * Show a list of the repository branches * * @return array List of branches */ public function getBranches() { static $cache = array(); if (array_key_exists($this->path, $cache)) { return $cache[$this->path]; } $branches = $this->getClient()->run($this, "branch"); $branches = explode("\n", $branches); $branches = array_filter(preg_replace('/[\*\s]/', '', $branches)); if (empty($branches)) { return $cache[$this->path] = $branches; } // Since we've stripped whitespace, the result "* (detached from " // and "* (no branch)" that is displayed in detached HEAD state // becomes "(detachedfrom" and "(nobranch)" respectively. if ((strpos($branches[0], '(detachedfrom') === 0) || ($branches[0] === '(nobranch)')) { $branches = array_slice($branches, 1); } return $cache[$this->path] = $branches; } /** * Return the current repository branch * * @return mixed Current repository branch as a string, or NULL if in * detached HEAD state. */ public function getCurrentBranch() { $branches = $this->getClient()->run($this, "branch"); $branches = explode("\n", $branches); foreach ($branches as $branch) { if ($branch[0] === '*') { if ((strpos($branch, '* (detached from ') === 0) || ($branch === '* (no branch)')) { return NULL; } return substr($branch, 2); } } } /** * Check if a specified branch exists * * @param string $branch Branch to be checked * @return boolean True if the branch exists */ public function hasBranch($branch) { $branches = $this->getBranches(); $status = in_array($branch, $branches); return $status; } /** * Create a new repository branch * * @param string $branch Branch name */ public function createBranch($branch) { $this->getClient()->run($this, "branch $branch"); } /** * Create a new repository tag * * @param string $tag Tag name */ public function createTag($tag, $message = null) { $command = "tag"; if ($message) { $command .= " -a -m '$message'"; } $command .= " $tag"; $this->getClient()->run($this, $command); } /** * Show a list of the repository tags * * @return array List of tags */ public function getTags() { static $cache = array(); if (array_key_exists($this->path, $cache)) { return $cache[$this->path]; } $tags = $this->getClient()->run($this, "tag"); $tags = explode("\n", $tags); array_pop($tags); if (empty($tags[0])) { return $cache[$this->path] = NULL; } return $cache[$this->path] = $tags; } /** * Show the amount of commits on the repository * * @return integer Total number of commits */ public function getTotalCommits($file = null) { if (defined('PHP_WINDOWS_VERSION_BUILD')) { $command = "rev-list --count --all $file"; } else { $command = "rev-list --all $file | wc -l"; } $commits = $this->getClient()->run($this, $command); return trim($commits); } /** * Show the repository commit log * * @return array Commit log */ public function getCommits($file = null) { $command = "log --pretty=format:\"%H%h%T%P%an%ae%at%cn%ce%ct\""; if ($file) { $command .= " $file"; } $logs = $this->getPrettyFormat($command); foreach ($logs as $log) { $commit = new Commit; $commit->importData($log); $commits[] = $commit; foreach ($this->statistics as $statistic) { $statistic->addCommit($commit); } } $this->setCommitsHaveBeenParsed(true); return $commits; } /** * Show the data from a specific commit. * * @param string $commitHash Hash of the specific commit to read data * * @return array Commit data */ public function getCommit($commitHash, $options = null) { $logs = $this->getClient()->run( $this, 'show --pretty=format:"%H' . '%h%T%P' . '%aN%aE' . '%at%cN%cE' . '%ct' . '' . '' . "\" $commitHash" ); $xmlEnd = strpos($logs, '') + 7; $commitInfo = substr($logs, 0, $xmlEnd); $commitData = substr($logs, $xmlEnd); $logs = explode("\n", $commitData); // Read commit metadata $format = new PrettyFormat(); $data = $format->parse($commitInfo); $commit = new Commit(); $commit->importData($data[0]); if ($commit->getParentsHash()) { $command = 'diff ' . $commitHash . '~1..' . $commitHash; $logs = explode("\n", $this->getClient()->run($this, $command)); } $commit->setDiffs($this->readDiffLogs($logs, $options)); return $commit; } /** * Read diff logs and generate a collection of diffs. * * @param array $logs Array of log rows * * @return array Array of diffs */ public function readDiffLogs(array $logs, $options = null) { if ($options === null) { $options = []; $options['showLines'] = true; } if (!isset($options['filename'])) { $options['filename'] = ''; } $diffs = []; $lineNumOld = 0; $lineNumNew = 0; foreach ($logs as $log) { // Skip empty lines if ($log == '') { continue; } if ('diff' === substr($log, 0, 4)) { if (isset($diff)) { if ($options['filename'] === '') { $diffs[] = $diff; } elseif ($options['filename'] === $diff->getFile()) { $diffs[] = $diff; } } $diff = new Diff(); if (preg_match('/^diff --[\S]+ a\/?(.+) b\/?/', $log, $name)) { $diff->setFile($name[1]); } continue; } if ('index' === substr($log, 0, 5)) { $diff->setIndex($log); continue; } if ('---' === substr($log, 0, 3)) { $diff->setOld($log); continue; } if ('+++' === substr($log, 0, 3)) { $diff->setNew($log); continue; } // Handle binary files properly. if ('Binary' === substr($log, 0, 6)) { $m = []; if (preg_match('/Binary files (.+) and (.+) differ/', $log, $m)) { $diff->setBinary(true); $diff->setOld('--- ' . $m[1]); $diff->setNew("+++ {$m[2]}"); } } if (!empty($log)) { switch ($log[0]) { case '@': // Set the line numbers preg_match('/@@ -([0-9]+)(?:,[0-9]+)? \+([0-9]+)/', $log, $matches); $lineNumOld = $matches[1] - 1; $lineNumNew = $matches[2] - 1; break; case '-': $lineNumOld++; break; case '+': $lineNumNew++; break; default: $lineNumOld++; $lineNumNew++; } } else { $lineNumOld++; $lineNumNew++; } if (isset($diff)) { if ($options['showLines']) { $diff->addLine($log, $lineNumOld, $lineNumNew); } $diff->lineCount++; } } if (isset($diff)) { if ($options['filename'] === '' || count($diffs) === 0) $diffs[] = $diff; } return $diffs; } /** * Get the current HEAD. * * @param $default Optional branch to default to if in detached HEAD state. * If not passed, just grabs the first branch listed. * @return string the name of the HEAD branch, or a backup option if * in detached HEAD state. */ public function getHead($default = null) { if ($default === null) { $client = $this->getClient(); $default = $client->getDefaultBranch(); } $file = ''; if (file_exists($this->getPath() . '/.git/HEAD')) { $file = file_get_contents($this->getPath() . '/.git/HEAD'); } elseif (file_exists($this->getPath() . '/HEAD')) { $file = file_get_contents($this->getPath() . '/HEAD'); } // Find first existing branch foreach (explode("\n", $file) as $line) { $m = array(); if (preg_match('#ref:\srefs/heads/(.+)#', $line, $m)) { if ($this->hasBranch($m[1])) { return $m[1]; } } } // If we were given a default branch and it exists, return that. if ($default !== null && $this->hasBranch($default)) { return $default; } // Otherwise, return the first existing branch. $branches = $this->getBranches(); if (!empty($branches)) { return current($branches); } // No branches exist - null is the best we can do in this case. return null; } /** * Extract the tree hash for a given branch or tree reference * * @param string $branch * @return string */ public function getBranchTree($branch) { $hash = $this->getClient()->run($this, "log --pretty=\"%T\" --max-count=1 $branch"); $hash = trim($hash, "\r\n "); return $hash ? : false; } /** * Get the Tree for the provided folder * * @param string $tree Folder that will be parsed * @return Tree Instance of Tree for the provided folder */ public function getTree($tree) { $tree = new Tree($tree, $this); $tree->parse(); return $tree; } /** * Get the Blob for the provided file * * @param string $blob File that will be parsed * @return Blob Instance of Blob for the provided file */ public function getBlob($blob) { return new Blob($blob, $this); } /** * Blames the provided file and parses the output. * * @param string $file File that will be blamed * * @return array Commits hashes containing the lines */ public function getBlame($file) { $blame = []; $logs = $this->getClient()->run($this, "blame --root -sl $file"); $logs = explode("\n", $logs); $i = 0; $previousCommit = ''; foreach ($logs as $log) { if ($log == '') { continue; } preg_match_all("/([a-zA-Z0-9]{40})\s+.*?([0-9]+)\)(.+)/", $log, $match); $currentCommit = $match[1][0]; if ($currentCommit != $previousCommit) { ++$i; $blame[$i] = [ 'line' => '', 'commit' => $currentCommit, 'commitShort' => substr($currentCommit, 0, 8), ]; } $blame[$i]['line'] .= $match[3][0] . PHP_EOL; $previousCommit = $currentCommit; } return $blame; } /** * Get the current Repository path * * @return string Path where the repository is located */ public function getPath() { return $this->path; } /** * Set the current Repository path * * @param string $path Path where the repository is located */ public function setPath($path) { $this->path = $path; } /** * Get the current Client instance * * @return Client Client instance */ public function getClient() { return $this->client; } /** * Set the Client * * @param Client $path Client instance */ public function setClient(Client $client) { $this->client = $client; return $this; } /** * Get and parse the output of a git command with a XML-based pretty format * * @param string $command Command to be run by git * @return array Parsed command output */ public function getPrettyFormat($command) { $output = $this->getClient()->run($this, $command); $format = new PrettyFormat; return $format->parse($output); } public function getShortHash($commit) { $shortHash = $this->getClient()->run($this, 'rev-parse --short ' . $commit); return $shortHash; } /** * Return true if the repo contains this commit. * * @param $commitHash Hash of commit whose existence we want to check * * @return bool Whether or not the commit exists in this repo */ public function hasCommit($commitHash) { $logs = $this->getClient()->run($this, "show $commitHash"); $logs = explode("\n", $logs); return strpos($logs[0], 'commit') === 0; } /** * Show Patches that where apllied to the selected file. * * @param string $file File path for which we will retrieve a list of patch logs * * @return array Collection of Commits data */ public function getCommitsLogPatch($file) { $record_delimiter = chr(hexdec('0x1e')); $file_patches = $this->getClient()->run( $this, 'log -p --pretty=format:"' . $record_delimiter . '%H' . '%h%T%P' . '%aN%aE' . '%at%cN%cE' . '%ct' . '' . '' . "\" -- $file" ); $patch_collection = []; foreach (preg_split('/(' . $record_delimiter . '\)/', $file_patches, null, PREG_SPLIT_NO_EMPTY) as $patches) { $patches = '' . $patches; $xmlEnd = strpos($patches, '') + 7; $commitInfo = substr($patches, 0, $xmlEnd); $commitData = substr($patches, $xmlEnd); $logs = explode("\n", $commitData); // Read commit metadata $format = new PrettyFormat(); $data = $format->parse($commitInfo); $commit = new Commit(); $commit->importData($data[0]); $commit->setDiffs($this->readDiffLogs($logs)); $patch_collection[] = $commit; } return $patch_collection; } /** * Show the repository commit log with pagination. * * @param string $file * @param int page * * @return array Commit log */ public function getPaginatedCommits($file = null, $page = 0) { $page = 15 * $page; $pager = "--skip=$page --max-count=15"; $command = "log $pager --pretty=format:\"%H" . '%h%T%P' . '%aN%aE' . '%at%cN' . '%cE' . '%ct' . '"'; if ($file) { $command .= " $file"; } try { $logs = $this->getPrettyFormat($command); } catch (\RuntimeException $e) { return []; } foreach ($logs as $log) { $commit = new Commit(); $commit->importData($log); $commits[] = $commit; } return $commits; } public function searchCommitLog($query, $branch) { $query = escapeshellarg($query); $query = strtr($query, ['[' => '\\[', ']' => '\\]']); $command = "log --grep={$query} -i --pretty=format:\"%H" . '%h%T%P' . '%aN%aE' . '%at%cN' . '%cE' . '%ct' . '"' . " $branch"; try { $logs = $this->getPrettyFormat($command); } catch (\RuntimeException $e) { return []; } foreach ($logs as $log) { $commit = new Commit(); $commit->importData($log); $commits[] = $commit; } return $commits; } public function searchTree($query, $branch) { if (empty($query)) { return null; } $query = preg_replace('/(--?[A-Za-z0-9\-]+)/', '', $query); $query = escapeshellarg($query); try { $results = $this->getClient()->run($this, "grep -i --line-number -- {$query} $branch"); } catch (\RuntimeException $e) { return false; } $results = explode("\n", $results); $searchResults = []; foreach ($results as $result) { if ($result == '') { continue; } preg_match_all('/([\w-._]+):([^:]+):([0-9]+):(.+)/', $result, $matches, PREG_SET_ORDER); if (isset($matches[0])) { $data['branch'] = $matches[0][1]; $data['file'] = $matches[0][2]; $data['line'] = $matches[0][3]; $data['match'] = $matches[0][4]; $searchResults[] = $data; } } return $searchResults; } public function getAuthorStatistics($branch) { $logs = $this->getClient()->run($this, 'log --pretty=format:"%aN||%aE" ' . $branch); if (empty($logs)) { throw new \RuntimeException('No statistics available'); } $logs = explode("\n", $logs); $logs = array_count_values($logs); arsort($logs); foreach ($logs as $user => $count) { $user = explode('||', $user); $data[] = ['name' => $user[0], 'email' => $user[1], 'commits' => $count]; } return $data; } /** * Create a TAR or ZIP archive of a git tree. * * @param string $tree Tree-ish reference * @param string $output Output File name * @param string $format Archive format */ public function createArchive($tree, $output, $format = 'zip') { $fs = new Filesystem(); $fs->mkdir(dirname($output)); $this->getClient()->run($this, "archive --format=$format --output='$output' $tree"); } /** * Return true if $path exists in $branch; return false otherwise. * * @param string $commitish commitish reference; branch, tag, SHA1, etc * @param string $path path whose existence we want to verify * * @return bool * * GRIPE Arguably belongs in Gitter, as it's generally useful functionality. * Also, this really may not be the best way to do this. */ public function pathExists($commitish, $path) { $output = $this->getClient()->run($this, "ls-tree $commitish '$path'"); if (strlen($output) > 0) { return true; } return false; } protected function changeRepo($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, $callback) { $temporaryDirectory = ''; $tempRepo = ''; $hadError = false; $command = ''; $output = ''; $outputs = []; $trace = null; try { $temporaryDirectory = (new TemporaryDirectory($cachePath))->create(); $client = $this->getClient(); $repoPath = realpath($this->getPath()); $tempRepo = $temporaryDirectory->path(); $output = $client->run($this, 'clone '. $repoPath . ' ' . $tempRepo); $this->setPath($tempRepo); $normalizedRepoFilePath = $this->isValidPath($tempRepo, $repoFilename, $outputs); $filename = realpath($tempRepo . DIRECTORY_SEPARATOR . $repoFilename); $command = "checkout $branch"; $output = $client->run($this, $command); array_push($outputs, $output); $message = $callback($client, $filename, $outputs, $tempRepo, $normalizedRepoFilePath); array_push($outputs, $message); $command = " -c \"user.name=$name\" -c \"user.email=$email\" commit -am \"$comment\" "; $output = $client->run($this, $command); array_push($outputs, $output); // $command = "commit -am \"$comment\""; // $output = $client->run($repository, $command); $command = "push"; $output = $client->run($this, $command); array_push($outputs, $output); $command = " rev-parse HEAD "; $lastCommit = $client->run($this, $command); $result = (object) [ 'status' => 'ok', 'output' => $message, 'outputs' => $outputs, 'last-commit' => $lastCommit, 'branch' => $branch, ]; return $result; } catch(\Throwable $e) { $hadError = $e; } finally { if ($temporaryDirectory !== '') { @$temporaryDirectory->delete(); } if ($hadError !== false) { $message = $hadError->getMessage(); if ($message === '') { $exceptionName = get_class($hadError); $message = "Received exception without message with type '{$exceptionName}'. " . $hadError->getMessage(); $trace = $hadError->getTrace(); } return ((object) [ 'status' => $message === '' ? 'ok' : 'error', 'error' => $message === '' ? false : true, //'temporaryDirectory' => $tempRepo, 'message' => $message, //'currentdir' => getcwd(), //'command' => $command, 'output' => $output, 'outputs' => $outputs, 'trace' => $trace, //'$filename' => $filename, //'$value' => $value, ]); } } } protected function isValidPath($tempRepo, $repoFilename, &$outputs) { $basePath = FileSystemPath::fromString($tempRepo); //array_push($outputs, "$basePath {$basePath}"); $repoFilenameItem = FileSystemPath::fromString($repoFilename); //array_push($outputs, "$repoFilenameItem {$repoFilenameItem}"); $normalizing = $basePath->resolve($repoFilenameItem); //array_push($outputs, "$normalizing {$normalizing}"); // array_unshift($outputs, '$normalizing: ' . $normalizing); $normalized = FileSystemPath::fromString($normalizing)->normalize(); //array_push($outputs, "$normalized {$normalized}"); // array_unshift($outputs, '$normalized: ' . $normalized); // array_unshift($outputs, '$normalizing type: ' . gettype($normalizing)); /// array_unshift($outputs, '$normalized type: ' . gettype($normalized)); $normalizedRepoFilePath = $normalized->__toString(); $validPath = strpos($normalizing->__toString(), $normalizedRepoFilePath ) !== false; //array_push($outputs, "$normalizing {$normalizing->__toString()}"); //array_push($outputs, "$normalizedRepoFilePath {$normalizedRepoFilePath}"); //array_push($outputs, (substr($normalizedRepoFilePath, 0, strlen($basePath)) . " $basePath")); if ($validPath === false || substr($normalizedRepoFilePath, 0, strlen($basePath)) != $basePath) { throw new \Exception("This '{$repoFilename}' path is invalid."); } return $normalizedRepoFilePath; } public function newFileBinary($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, $override, $phpUploadFile) { /* return (object)[ 'filename' => $repoFilename, 'email' => $email, 'name' => $name, 'comment' => $comment, 'upload-file' => $phpUploadFile, 'override' => $override, ]; */ return $this->changeRepo($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, function ($client, $filename, &$outputs, $tempRepo, $normalizedRepoFilePath) use ($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, $override, $phpUploadFile) { // array_unshift($outputs, $cachePath); // array_unshift($outputs, $repoFilename); /* array_unshift($outputs, '$tempRepo: ' . $tempRepo); array_unshift($outputs, '$repo: ' . $repo); array_unshift($outputs, '$branch: ' . $branch); array_unshift($outputs, '$repoFilename: ' . $repoFilename); array_unshift($outputs, '$name: ' . $name); array_unshift($outputs, '$email: ' . $email); array_unshift($outputs, '$comment: ' . $comment); array_unshift($outputs, '$filename: ' . $filename); array_unshift($outputs, '$repoFilename: ' . $repoFilename); $basePath = FileSystemPath::fromString($tempRepo); $repoFilenameItem = FileSystemPath::fromString($repoFilename); $normalizing = $basePath->resolve($repoFilenameItem); array_unshift($outputs, '$normalizing: ' . $normalizing); $normalized = FileSystemPath::fromString($normalizing)->normalize(); array_unshift($outputs, '$normalized: ' . $normalized); array_unshift($outputs, '$normalizing type: ' . gettype($normalizing)); array_unshift($outputs, '$normalized type: ' . gettype($normalized)); $validPath = strpos($normalizing->__toString(), $normalized->__toString()) !== false; array_unshift($outputs, 'includes current path in search: ' . ($validPath ? 'true' : 'false')); return $repoFilename; */ $wasItNonExisting = !realpath($normalizedRepoFilePath); array_push($outputs, $wasItNonExisting ); array_push($outputs, $repoFilename); array_push($outputs, $normalizedRepoFilePath); array_push($outputs, $override); if (substr($repoFilename, -1) == '\\' || substr($repoFilename, -1) == '/') { return "The file can't end with this file name: {$repoFilename}"; } else { if (!$wasItNonExisting && !$override) { throw new \Exception("This file is already existing: {$repoFilename}"); } if ($wasItNonExisting && $override) { @unlink($normalizedRepoFilePath); } @mkdir(dirname($normalizedRepoFilePath), 0777, true); move_uploaded_file( $phpUploadFile['tmp_name'], $normalizedRepoFilePath); if ($wasItNonExisting) { $command = " add . "; $output = $client->run($this, $command); array_push($outputs, $output); return "Created new binary file : {$repoFilename}"; } else { return "Overridden binary file : {$repoFilename}"; } // echo "path is file "; } }); /* File mime type check is not implemented. $objectResult = $repository->newFileBinary($app->getCachePath(), $repo, $branch, $filename, $name, $email, $comment, $request->get('override') === '1' ? true : false, $_FILES[0]); { "filename": "2017-Electronic-Diversity-Visa Lottery-Kriszti.jpg", "email": "alabard@gmail.com", "name": "patrikx3", "comment": "P3X Gitlist Commit New binary", "upload-file": { "name": "2017-Electronic-Diversity-Visa Lottery-Kriszti.jpg", "type": "image\/jpeg", "tmp_name": "\/tmp\/phpzGVxqY", "error": 0, "size": 125931 }, "override": "1" } { "filename": "krip.krip", "email": "alabard@gmail.com", "name": "patrikx3", "comment": "P3X Gitlist Commit New binary", "upload-file": { "name": "krip.krip", "type": "", "tmp_name": "", "error": 1, "size": 0 }, "override": "1" } { "filename": "corifeus-colors.txt", "email": "alabard@gmail.com", "name": "patrikx3", "comment": "P3X Gitlist Commit New binary", "upload-file": { "name": "corifeus-colors.txt", "type": "text\/plain", "tmp_name": "\/tmp\/phpxjBzT8", "error": 0, "size": 133 }, "override": "1" } */ } public function changeFile($cachePath, $repo, $branch, $repoFilename, $value, $name, $email, $comment) { return $this->changeRepo($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, function($client, $filename, $outputs, $tempRepo, $normalizedRepoFilePath) use ($value) { //$originalFileContent = file_get_contents($filename); file_put_contents($normalizedRepoFilePath, $value); return ''; }); } public function deleteFile($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment) { return $this->changeRepo($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, function($client, $filename, $outputs, $tempRepo, $normalizedRepoFilePath) { //$originalFileContent = file_get_contents($filename); @unlink($normalizedRepoFilePath); return 'Deleted ' . $filename; }); } public function newFileOrDirectory($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment) { return $this->changeRepo($cachePath, $repo, $branch, $repoFilename, $name, $email, $comment, function ($client, $filename, &$outputs, $tempRepo, $normalizedRepoFilePath) use ($repoFilename, $repo, $branch, $name, $email, $comment) { // array_unshift($outputs, $cachePath); // array_unshift($outputs, $repoFilename); /* array_unshift($outputs, '$tempRepo: ' . $tempRepo); array_unshift($outputs, '$repo: ' . $repo); array_unshift($outputs, '$branch: ' . $branch); array_unshift($outputs, '$repoFilename: ' . $repoFilename); array_unshift($outputs, '$name: ' . $name); array_unshift($outputs, '$email: ' . $email); array_unshift($outputs, '$comment: ' . $comment); array_unshift($outputs, '$filename: ' . $filename); array_unshift($outputs, '$repoFilename: ' . $repoFilename); $basePath = FileSystemPath::fromString($tempRepo); $repoFilenameItem = FileSystemPath::fromString($repoFilename); $normalizing = $basePath->resolve($repoFilenameItem); array_unshift($outputs, '$normalizing: ' . $normalizing); $normalized = FileSystemPath::fromString($normalizing)->normalize(); array_unshift($outputs, '$normalized: ' . $normalized); array_unshift($outputs, '$normalizing type: ' . gettype($normalizing)); array_unshift($outputs, '$normalized type: ' . gettype($normalized)); $validPath = strpos($normalizing->__toString(), $normalized->__toString()) !== false; array_unshift($outputs, 'includes current path in search: ' . ($validPath ? 'true' : 'false')); return $repoFilename; */ $existing = realpath($normalizedRepoFilePath); if ($existing) { throw new \Exception("This path is already existing: {$repoFilename}"); } if (substr($repoFilename, -1) == '\\' || substr($repoFilename, -1) == '/') { //echo "is path "; // if (realpath($normalizedRepoFilePath) === FALSE) { if (@mkdir($normalizedRepoFilePath, 0777, true)) { // echo "path is not existing "; touch($normalizedRepoFilePath . '/.gitkeep'); $command = " add . "; $output = $client->run($this, $command); array_push($outputs, $output); return "Created new directory (including .gitkeep file): {$repoFilename}"; } return "Failed to create the new directory: {$repoFilename}"; //} else { // echo "path is existing "; // throw new \Exception("This path is already existing."); //} } else { @mkdir(dirname($normalizedRepoFilePath), 0777, true); touch($normalizedRepoFilePath ); $command = " add . "; $output = $client->run($this, $command); array_push($outputs, $output); return "Created new file : {$repoFilename}"; // echo "path is file "; } }); } } src/Gitter/Statistics/000077500000000000000000000000001516067322700152465ustar00rootroot00000000000000src/Gitter/Statistics/Contributors.php000066400000000000000000000016411516067322700204560ustar00rootroot00000000000000getAuthor()->getEmail(); $commitDate = $commit->getCommiterDate()->format('Y-m-d'); if (!isset($this->items[$email])) { $this->items[$email] = new Collection; } $this->items[$email]->items[$commitDate][] = $commit; ksort($this->items[$email]->items); } public function sortCommits() { uasort($this->items, function ($sortA, $sortB) { if (count($sortA) === count($sortB)) { return 0; } return count($sortA) > count($sortB) ? -1 : 1; }); } }src/Gitter/Statistics/Date.php000066400000000000000000000007461516067322700166430ustar00rootroot00000000000000getCommiterDate()->format('Y-m-d'); $this->items[$day][] = $commit; } public function sortCommits() { ksort($this->items); } }src/Gitter/Statistics/Day.php000066400000000000000000000007411516067322700164760ustar00rootroot00000000000000getCommiterDate()->format('N'); $this->items[$day][] = $commit; } public function sortCommits() { ksort($this->items); } }src/Gitter/Statistics/Hour.php000066400000000000000000000007451516067322700167020ustar00rootroot00000000000000getCommiterDate()->format('H'); $this->items[$hour][] = $commit; } public function sortCommits() { ksort($this->items); } }src/Gitter/Statistics/StatisticsInterface.php000066400000000000000000000002741516067322700217350ustar00rootroot00000000000000items; } /** * @param array $items */ public function setItems($items) { $this->items = $items; } /** * {@inheritdoc} */ public function offsetExists($offset) { return isset($this->items[$offset]); } /** * {@inheritdoc} */ public function offsetGet($offset) { return isset($this->items[$offset]); } /** * {@inheritdoc} */ public function offsetSet($offset, $value) { if (is_null($offset)) { $this->items[] = $value; } else { $this->items[$offset] = $value; } } /** * {@inheritdoc} */ public function offsetUnset($offset) { unset($this->items[$offset]); } /** * {@inheritdoc} */ public function getIterator() { return new \ArrayIterator($this->items); } /** * {@inheritdoc} */ public function count() { return count($this->items); } }src/Gitter/Util/DateTime.php000066400000000000000000000034711516067322700162430ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Gitter\Util; /** * Fixes the issue that the $timezone parameter and the current timezone are ignored when * the $time parameter either is a UNIX timestamp (e.g. @946684800) or specifies a timezone * (e.g. 2010-01-28T15:00:00+02:00). * * @link https://github.com/klaussilveira/gitlist/issues/140 */ class DateTime extends \DateTime { /** * @const The regular expression for an UNIX timestamp */ const UNIX_TIMESTAMP_PATTERN = '/^@\d+$/'; /** * @param string $time A date/time string. * @param DateTimeZone $timezone A DateTimeZone object representing the desired time zone. * @return DateTime A new DateTime instance. * @link http://php.net/manual/en/datetime.construct.php */ public function __construct($time = 'now', \DateTimeZone $timezone = null) { if ($timezone) { parent::__construct($time, $timezone); } else { parent::__construct($time); } if ($this->isUnixTimestamp($time)) { if (!$timezone) { $timezone = new \DateTimeZone(date_default_timezone_get()); } $this->setTimezone($timezone); } } /** * Checks if an UNIX timestamp is passed. * * @param string $time A date/time string. * @return bool Returns true if the $time parameter is a UNIX timestamp */ protected function isUnixTimestamp($time) { if (preg_match(self::UNIX_TIMESTAMP_PATTERN, $time)) { return true; } return false; } } src/browser/000077500000000000000000000000001516067322700133415ustar00rootroot00000000000000src/browser/bundle.js000066400000000000000000000067131516067322700151570ustar00rootroot00000000000000window.gitlist.codemirrorTheme = { light: 'idea', dark: 'dracula', } require('codemirror/lib/codemirror.css') require(`codemirror/theme/idea.css`) require(`codemirror/theme/dracula.css`) //require('../less/style.less') require('@fortawesome/fontawesome-free/js/all') /* //console.log(fontawesome); //console.log(fontawesome.default); const faSolid = require('@fortawesome/fontawesome-free-solid')['default'] const faRegular = require('@fortawesome/fontawesome-free-regular')['default'] const faBrands = require('@fortawesome/fontawesome-free-brands')['default'] fontawesome.library.add(faSolid) fontawesome.library.add(faRegular) fontawesome.library.add(faBrands) */ window.gitlist.snapckbarLongTimeout = 20000; global.jQuery = require('jquery') global.$ = global.jQuery; require('snackbarjs'); require('jquery.redirect'); require('bootstrap'); global.marked = require('marked') global.htmlEncode = require('js-htmlencode') global.CodeMirror = require('codemirror'); require('codemirror/addon/selection/active-line.js') require('codemirror/addon/mode/simple'); require('codemirror/addon/mode/multiplex'); require('codemirror/mode/cmake/cmake'); require('codemirror/mode/clike/clike'); require('codemirror/mode/css/css'); require('codemirror/mode/dockerfile/dockerfile'); require('codemirror/mode/go/go'); require('codemirror/mode/handlebars/handlebars'); require('codemirror/mode/htmlmixed/htmlmixed'); require('codemirror/mode/javascript/javascript'); require('codemirror/mode/jsx/jsx'); require('codemirror/mode/perl/perl'); require('codemirror/mode/php/php'); require('codemirror/mode/powershell/powershell'); require('codemirror/mode/python/python'); require('codemirror/mode/properties/properties'); require('codemirror/mode/ruby/ruby'); require('codemirror/mode/sass/sass'); require('codemirror/mode/shell/shell'); require('codemirror/mode/vbscript/vbscript'); require('codemirror/mode/groovy/groovy'); require('codemirror/mode/erlang/erlang'); require('codemirror/mode/ecl/ecl'); require('codemirror/mode/coffeescript/coffeescript'); require('codemirror/mode/clojure/clojure'); require('codemirror/mode/diff/diff'); require('codemirror/mode/smalltalk/smalltalk'); require('codemirror/mode/rust/rust'); require('codemirror/mode/lua/lua'); require('codemirror/mode/haskell/haskell'); require('codemirror/mode/markdown/markdown'); require('codemirror/mode/scheme/scheme'); require('codemirror/mode/r/r'); require('codemirror/mode/rst/rst'); require('codemirror/mode/ntriples/ntriples'); require('codemirror/mode/pascal/pascal'); require('codemirror/mode/sql/sql'); require('codemirror/mode/swift/swift'); require('codemirror/mode/twig/twig'); require('codemirror/mode/vue/vue'); require('codemirror/mode/xml/xml'); require('codemirror/mode/xquery/xquery'); require('codemirror/mode/yaml/yaml'); require('eve-raphael/eve.js'); global.Raphael = require('raphael') global.twemoji = require('twemoji').default const prodDir = require('../../package').corifeus["prod-dir"]; global.twemoji.base = `${prodDir}/twemoji/`; require('./js/network.js') require('./js/gitgraph.js/gitgraph.css') require('./js/gitgraph.js/gitgraph.js') require('./js/tree') require('./js/treegraph') require('./js/markdown') require('./js/clone-buttons') require('./js/paginate') require('./js/browser') require('./js/index.js') require('./js/file') require('./js/theme-switcher.js') require('./js/commit') require('./js/commits-list') require('./js/file-fragment') require('./js/change-log') require('./js/todo') require('./js/global') src/browser/grunt/000077500000000000000000000000001516067322700145005ustar00rootroot00000000000000src/browser/grunt/less.js000066400000000000000000000110421516067322700160020ustar00rootroot00000000000000const fs = require('fs'); const fsExtra = require('fs-extra'); const glob = require('glob'); const crypto = require('crypto'); const path = require('path'); const cwd = path.resolve(`${process.cwd()}`) const prodDir = require('../../../package').corifeus["prod-dir"]; let lessLastHash; let lessFiles; const filesLessCache = `${cwd}/build/less/file-less.json`; const lessSettings = (grunt) => { return { options: { sourceMap: true, compress: true, }, get files() { const files = glob.sync(`${cwd}/src/browser/less/**/*.*`) let string = '' for (let filename of files) { string += fs.readFileSync(filename, 'utf8') } const lessHash = crypto.createHash('sha256').update(string).digest("hex"); if (lessLastHash === lessHash) { grunt.log.writeln(`less hash is the same - ${lessHash}`) return lessFiles }/* else if (fs.existsSync(filesLessCacheBuild) && fs.existsSync(filesLessCache) && fs.readFileSync(cssPostfixFilename, 'utf8').toString() === lessHash) { grunt.log.writeln(`less hash is the same in different process, using file less cache - ${lessHash}`) return fs.readFileSync(filesLessCache, 'utf8').toString(); }*/ grunt.log.writeln(`less hash generating new build - ${lessHash}`) lessLastHash = lessHash; const pkgFilename = './package.json'; const pkg = fsExtra.readJsonSync(pkgFilename); pkg.corifeus.postfix = lessHash //fsExtra.writeJsonSync(pkgFilename, pkg) fs.writeFileSync(pkgFilename, JSON.stringify(pkg, null, 4), 'utf8') grunt.log.writeln('The css postfix file has been saved!'); const generateLessFiles = () => { const themeDir = './src/browser/less/theme'; const filesLess = {} const root = './node_modules/bootswatch'; const watches = fs.readdirSync(root); const themes = [ 'default', 'solar', ]; const excluded = ['fonts']; // css with random const themeCss = { 'bootstrap-default': `${prodDir}/css/bootstrap-default.${lessHash}.css`, 'bootstrap-solar': `${prodDir}/css/bootstrap-solar.${lessHash}.css`, } for (let path of watches) { const stat = fs.statSync(`${root}/${path}`); if (stat.isDirectory() && !excluded.includes(path)) { themes.push(path); themeCss[`bootstrap-${path}`] = `${prodDir}/css/bootstrap-${path}.${lessHash}.css`; } } // -- css with random // less with random fsExtra.ensureDirSync(themeDir); const pathFont = 'https://fonts.googleapis.com/css?family=Roboto:300,400,700' for (let theme of themes) { const less = `${themeDir}/${theme}.less`; if (theme === 'default') { fs.writeFileSync(less, ` @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../default"; `) } else if (theme === 'solar') { fs.writeFileSync(less, ` @path: '${pathFont}'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "solar/variables"; @import "solar/bootswatch"; @import "../default"; `) } else { fs.writeFileSync(less, ` @path: '${pathFont}'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/${theme}/variables"; @import "../../../../node_modules/bootswatch/${theme}/bootswatch"; @import "../default"; `) } // console.log(less) filesLess[`public/${prodDir}/css/bootstrap-${theme}.${lessHash}.css`] = less; } // -- less with random fs.writeFileSync(`./src/browser/js/themes.js`, ` const themes = ${JSON.stringify(themeCss, null, 4)}; module.exports = themes; `); fsExtra.outputJsonSync(filesLessCache, filesLess) return filesLess // grunt.log.writeln(JSON.stringify(filesLess, null, 4)) } lessFiles = generateLessFiles(); return lessFiles; } }; } module.exports.lessSettings = lessSettings;src/browser/js/000077500000000000000000000000001516067322700137555ustar00rootroot00000000000000src/browser/js/blame.js000066400000000000000000000000411516067322700153660ustar00rootroot00000000000000/* It uses the file-fragement */src/browser/js/browser.js000066400000000000000000000063031516067322700160000ustar00rootroot00000000000000$(function() { const menu = $('#p3x-gitlist-branch-list-container'); if (menu.length > 0) { // const menuResponsive = require('./menu-responsive') // menuResponsive({ // menuList: $('#p3x-gitlist-branch-list-dropdown-menu'), // }) const List = require('list.js') const mainId = 'p3x-gitlist-branch-list' const branchListId = 'p3x-gitlist-list-branch' const tagListId = 'p3x-gitlist-list-tag' const debounce = require('lodash/debounce') const path = window.gitlist.getPath() const baseUrl = `${window.gitlist.basepath}/${window.gitlist.repo}` const search_query = window.gitlist.search_query; const urls = { tree: (options) => { return `${baseUrl}/tree/${options.checkout}`; }, commits: (options) => { return `${baseUrl}/commits/${options.checkout}/${path}`; }, commit: (options) => { return `${baseUrl}/commits/${options.checkout}/${path}`; }, stats: (options) => { return `${baseUrl}/stats/${options.checkout}`; }, network: (options) => { return `${baseUrl}/network/${options.checkout}`; }, blob: (options) => { return `${baseUrl}/blob/${options.checkout}/${path}`; }, blame: (options) => { return `${baseUrl}/blame/${options.checkout}/${path}`; }, treegraph: (options) => { return `${baseUrl}/treegraph/${options.checkout}`; }, search: (options) => { return { url: `${baseUrl}/tree/${options.checkout}/search?query=${search_query}`, post: true, } }, searchcommits: (options) => { return { url: `${baseUrl}/commits/${options.checkout}/search?query=${search_query}`, post: true, }; }, } window.gitlist.browserClick = (options) => { let result = urls[window.gitlist.browse_type](options); if (typeof result === 'string') { result = { url: result } } if (!result.hasOwnProperty('post')) { location = result.url; } else { $.redirect(result.url); } } if ($(`#${mainId}`).length) { const listBranchOptions = { valueNames: ['item'], indexAsync: true, }; const branchList = new List(branchListId, listBranchOptions); const tagList = new List(tagListId, listBranchOptions); const input = $('#p3x-gitlist-branch-list-search'); const debouncedKeyup = debounce(() => { const search = input.val().trim(); branchList.search(search) if (tagList.hasOwnProperty('search')) { tagList.search(search) } }, 250) input.keyup(debouncedKeyup) } } }) src/browser/js/change-log.js000066400000000000000000000012551516067322700163220ustar00rootroot00000000000000let $changelogModal let changelogHtml; window.gitlist.changeLog = async() => { if (changelogHtml === undefined) { try { const response = await $.ajax('https://raw.githubusercontent.com/patrikx3/gitlist/master/changelog.md') const $changelogModalBody = $('#p3x-gitlist-modal-changelog-body') changelogHtml = window.gitlist.renderMarkdown({ markdown: response }) $changelogModalBody.html(changelogHtml); } catch(e) { window.gitlist.ajaxErrorHandler(e) } } $changelogModal.modal('show') } $(async () => { $changelogModal = $('#p3x-gitlist-modal-changelog') }) src/browser/js/clone-buttons.js000066400000000000000000000033221516067322700171070ustar00rootroot00000000000000$(function() { const cloneButtonSSH = $('#clone-button-ssh'); const cloneButtonHTTP = $('#clone-button-http'); const cloneInputSSH = $('#clone-input-ssh'); const cloneInputHTTP = $('#clone-input-http'); const debounce = require('lodash/debounce') const copy = debounce(($input) => { const input = $input[0]; input.select() document.execCommand("Copy"); $.snackbar({ htmlAllowed: true, content: ` This URL is in your clipboard:
${input.value}` }); }, 250); const copyCloneInputSSH = () => { copy(cloneInputSSH) } const copyCloneInputHTTP = () => { copy(cloneInputHTTP) } cloneInputSSH.click(copyCloneInputSSH) cloneInputHTTP.click(copyCloneInputHTTP) let cloneButtonSSHInit = false; cloneButtonSSH.click(function() { if(cloneButtonSSH.hasClass('active')) { return; } if (cloneButtonSSHInit) { copyCloneInputSSH(); } cloneButtonSSHInit = true; cloneButtonSSH.addClass('active'); cloneInputSSH.show(); cloneButtonHTTP.removeClass('active'); cloneInputHTTP.hide(); }); let cloneButtonHTTPInit = false; cloneButtonHTTP.click(function() { if(cloneButtonHTTP.hasClass('active')) { return; } if (cloneButtonHTTPInit) { copyCloneInputHTTP() } cloneButtonHTTPInit = true; cloneButtonHTTP.addClass('active'); cloneInputHTTP.show(); cloneButtonSSH.removeClass('active'); cloneInputSSH.hide(); }); cloneButtonSSH.click(); cloneButtonHTTP.click(); }) src/browser/js/commit.js000066400000000000000000000073471516067322700156160ustar00rootroot00000000000000$(() => { const $diffEditors = $('.p3x-gitlist-diff-container'); const $commitMessage = $('#p3x-gitlist-commit-heading'); //console.log($commitMessage) if ($commitMessage.length) { const html = marked($commitMessage.text().trim(), { renderer: window.gitlist.markdownRenderer, }); const twemojiSettings = require('./settings').twemoji; $commitMessage.html(twemoji.parse(html, twemojiSettings)); } if ($diffEditors.length > 0) { for (let diffEditor of $diffEditors) { const $editableHover = $('#' + diffEditor.dataset.diffId); const $diffEditor = $(diffEditor); $editableHover.on('click', () => { const url = new URL(location) $diffEditor.toggle() $editableHover.toggleClass('active') if (diffEditor.dataset.loaded) { return; } const loopIndex = diffEditor.dataset.loopIndex; //console.log(loopIndex) diffEditor.dataset.loaded = true url.searchParams.append('ajax', '1') url.searchParams.append('filename', diffEditor.dataset.filename) const loader = $(`#p3x-gitlist-commit-diff-loader-${loopIndex}`) const loaderAjax = $(`#p3x-gitlist-commit-diff-loader-ajax-${loopIndex}`) const loaderWebworker = $(`#p3x-gitlist-commit-diff-loader-webworker-${loopIndex}`) const scroller = $(`#p3x-gitlist-commit-diff-scroller-${loopIndex}`) scroller.css('max-height', window.gitlist.editorMaxHeight) $.ajax(url.toString()).then(function (diffsResponseJson) { if (typeof diffsResponseJson !== 'object') { const sendErrorMessage = `${window.gitlist.basepath}/json-error`; console.log(sendErrorMessage); $.redirect(sendErrorMessage, { error: diffsResponseJson, }) } else { const diffs = diffsResponseJson[0]; loaderAjax.hide() loaderWebworker.show() const worker = new Worker(`${window.gitlist.basepath}/web-worker/commit-diff.js`); worker.addEventListener('message', function (event) { loader.hide(); scroller.append(event.data) worker.terminate() //console.log('worker.onmessage', event.data) }) /* for(let diffLineIndex in diffs.lines) { diffs.lines[diffLineIndex].line = htmlEncode(diffs.lines[diffLineIndex].line) } */ worker.postMessage({ diffs : diffs, basepath: window.gitlist.basepath, htmlEncode: window.htmlEncode.toString(), }); } }).catch(window.gitlist.ajaxErrorHandler) }) } const isStringInt = require('is-string-int') if(isStringInt(location.hash.substr(1))) { const diff = parseInt(location.hash.substr(1)) const position = `p3x-gitlist-diff-${diff}` const element = document.getElementById(position); const diffButton = $(`#p3x-gitlist-diff-data-${diff}`) setTimeout(() => { window.gitlist.scrollIntoView(element) diffButton.click() }, 500) } } })src/browser/js/commits-list.js000066400000000000000000000020141516067322700167340ustar00rootroot00000000000000$(() => { const constructMarkdown = () => { const $commitListTables = $('.p3x-gitlist-commits-list:not(.p3x-gitlist-commits-list-rendered)'); if ($commitListTables.length > 0) { for(let commitTable of $commitListTables) { const $commitTable = $(commitTable) $commitTable.addClass('p3x-gitlist-commits-list-rendered') //console.log(commitTable) const $markedItems = $commitTable.find('.p3x-gitlist-commits-list-message') for(let markedItem of $markedItems) { //console.log(markedItem) const $markedItem = $(markedItem) const html = window.gitlist.renderMarkdown({ markdown: $markedItem.html() }) //console.log(html) $markedItem.html(html) } } } } constructMarkdown(); window.gitlist.constructCommitsListConstructMarkdown = constructMarkdown; })src/browser/js/file-fragment.js000066400000000000000000000057741516067322700170500ustar00rootroot00000000000000$(() => { const $fragmentFilePanel = $('.p3x-gitlist-file-fragment-panel') if ($fragmentFilePanel.length > 0) { let $buttonToggleCodeMirrorCurrent; let $textareaCurrent; let $textCurrent for(let fragmentFile of $fragmentFilePanel) { const index = fragmentFile.dataset.index const $buttonToggleCodeMirror = $(`#p3x-gitlilst-file-fragment-heading-button-codemirror-${ index }`) const $text = $(`#p3x-gitlist-file-fragment-text-${ index }`) const $textarea = $(`#p3x-gitlist-file-fragment-codemirror-${ index }`) const $buttonEditor = $(`#p3x-gitlilst-file-fragment-heading-button-edit-${ index}`) if ($buttonEditor.length > 0) { $buttonEditor.on('click', function(event) { const url = $buttonEditor.get(0).dataset.url location = url; }) } $buttonToggleCodeMirror.on('click', function(event) { if ($buttonToggleCodeMirrorCurrent !== undefined) { $('.CodeMirror-wrap').remove() const wasActive = $buttonToggleCodeMirror.hasClass('active') window.gitlist.fragmentFileCodeMirror = undefined $buttonToggleCodeMirrorCurrent.removeClass('active') $textCurrent.removeClass('hidden') $textareaCurrent.addClass('hidden') if (wasActive) { return; } } /* const position = $buttonToggleCodeMirror.get(0).dataset.commitShort const hash = `#${position}` //history.pushState(null, document.title,); location.hash = hash $buttonToggleCodeMirror.get(0).scrollIntoView() */ $buttonToggleCodeMirrorCurrent = $buttonToggleCodeMirror $textCurrent = $text; $textareaCurrent = $textarea; $text.addClass('hidden'); $textarea.removeClass('hidden') $buttonToggleCodeMirror.addClass('active') // const maxSize = window.gitlist.codemirror_full_limit; // const size = Math.ceil($textarea.get(0).value.length / 1024); // const codeMirrorHeight = window.gitlist.editorMaxHeight; const cm = CodeMirror.fromTextArea($textarea.get(0), { styleActiveLine: true, styleSelectedText: true, value: fragmentFile.value, lineNumbers: true, matchBrackets: true, lineWrapping: true, readOnly: true, height: 'auto', mode: fragmentFile.dataset.mode, theme: window.gitlist.getActualThemeCodemirror(), }); window.gitlist.fragmentFileCodeMirror = cm }) } } })src/browser/js/file.js000066400000000000000000000250541516067322700152400ustar00rootroot00000000000000$(function() { const Cookies = require('js-cookie') const errorHandler = window.gitlist.ajaxErrorHandler; const gitHelperAjax = window.gitlist.gitHelperAjax; const $modalDelete = $('#p3x-gitlist-modal-delete') if ($modalDelete.length === 0 ) { return; } const $buttonDelete = $('#p3x-gitlist-file-delete') const $buttonDeleteSure = $('#p3x-gitlist-modal-delete-confirm') const $formDeleteForm = $('#p3x-gitlist-modal-delete-form') const $deleteInputName = $('#p3x-gitlist-modal-delete-name'); const $deleteInputEmail = $('#p3x-gitlist-modal-delete-email'); const $deleteInputComment = $('#p3x-gitlist-modal-delete-comment'); const inputs = { name: $deleteInputName, email: $deleteInputEmail, comment: $deleteInputComment, } $buttonDelete.click(() => { if (!window.gitlist.changeableCommit()) { return } $modalDelete.modal('show') }) $formDeleteForm[0].addEventListener('submit', async(ev) => { ev.preventDefault(); if($formDeleteForm[0].checkValidity() === false) { window.gitlist.invalidSnackbarCommit() return; } try { const json = await gitHelperAjax({ modal: $modalDelete, action: 'delete', inputs: inputs, }) if (window.gitlist.gitNewPush(json)) { return } } catch(e) { errorHandler(e) } }) const sourceCode = $('#p3x-gitlist-file-editor'); if (sourceCode.length) { let originalCode = ''; let disableFull = false; const cookieName = 'p3x-gitlist-codemirror-size' const currentSizing = Cookies.get(cookieName) const $codeCodeMirroNormal = $('#p3x-gitlist-file-codemirror'); const $codeCodeMirrorBig = $('#p3x-gitlist-file-codemirror-exceeded') let value = sourceCode.text(); const maxSize = window.gitlist.codemirror_full_limit; const size = Math.ceil(value.length / 1024); let cm; const createCodeMirror = () => { if (size > maxSize) { disableFull = true; $codeCodeMirroNormal.hide(); $codeCodeMirrorBig.show(); } else { $codeCodeMirroNormal.show(); } const mode = sourceCode.attr('language'); const pre = sourceCode.get(0); const $codeMirror = $('.CodeMirror'); const $buttonScroll = $('#p3x-gitlist-file-button-scroll'); const $buttonFull = $('#p3x-gitlist-file-button-full'); const $buttonEdit = $('#p3x-gitlist-file-button-edit'); const $buttonEditCancel = $('#p3x-gitlist-file-button-edit-cancel'); const $buttonEditSave = $('#p3x-gitlist-file-button-edit-save') const $buttonDelete = $('#p3x-gitlist-file-delete') const codeMirrorHeight = window.gitlist.editorMaxHeight; $buttonEditCancel.hide(); $buttonEditSave.hide(); $buttonEdit.click(() => { if (!window.gitlist.changeableCommit()) { return } // buttonEditRow.show(); $buttonEdit.hide(); $buttonDelete.hide() $buttonEditCancel.show(); $buttonEditSave.show(); gitlist.viewer.setOption('readOnly', false) originalCode = gitlist.viewer.getValue() gitlist.viewer.focus(); $.snackbar({ content: `Editing`, }) }) const validateCodeIsSame = (snack = true) => { value = gitlist.viewer.getValue(); if (originalCode === value) { if (snack) { $.snackbar({ content: 'The code has not changed. No saving.', }) } return true; } return false; } const close = () => { $buttonDelete.show() $buttonEdit.show(); $buttonEditSave.hide(); $buttonEditCancel.hide(); gitlist.viewer.setOption('readOnly', true) } $buttonEditCancel.click(() => { if (!validateCodeIsSame(false)) { gitlist.viewer.setValue(originalCode) $.snackbar({ htmlAllowed: true, content: 'The changes are reverted.', }) } close(); }) const $commitModal = $('#p3x-gitlist-modal-commit'); $buttonEditSave.click(async () => { if (validateCodeIsSame()) { return; } $commitModal.modal('show') }) const $commitInputName = $('#p3x-gitlist-modal-commit-name'); const $commitInputEmail = $('#p3x-gitlist-modal-commit-email'); const $commitInputComment = $('#p3x-gitlist-modal-commit-comment'); const $commitForm = $('#p3x-gitlist-modal-commit-form'); const inputs = { name: $commitInputName, email: $commitInputEmail, comment: $commitInputComment, } //const $commitCommitPushButton = $('#p3x-gitlist-modal-commit-push') $commitForm[0].addEventListener('submit', async(ev) => { ev.preventDefault(); if (validateCodeIsSame()) { return; } if($commitForm[0].checkValidity() === false) { window.gitlist.invalidSnackbarCommit() return; } /* $.snackbar({ htmlAllowed: true, content: '  Saving ...' }) */ try { await gitHelperAjax({ modal: $commitModal, action: 'save', inputs: inputs, data: { value: value }, }) originalCode = value; close(); $.snackbar({ htmlAllowed: true, content: '  The file is saved.', }) } catch(e) { errorHandler(e); } }) const setScroll = () => { $buttonFull.removeClass('active') $buttonScroll.addClass('active') $codeMirror.css('height', codeMirrorHeight) gitlist.viewer.setSize(null, codeMirrorHeight); if (!disableFull) { Cookies.set(cookieName, 'scroll', window.gitlist.cookieSettings) } } $buttonScroll.click(setScroll) const setFull = () => { $buttonScroll.removeClass('active') $buttonFull.addClass('active') $codeMirror.css('height', 'auto') gitlist.viewer.setSize(null, '100%'); Cookies.set(cookieName, 'full', window.gitlist.cookieSettings) } $buttonFull.click(setFull) cm = CodeMirror(function(elt) { pre.parentNode.replaceChild(elt, pre); }, { styleActiveLine: true, styleSelectedText: true, value: value, lineNumbers: true, matchBrackets: true, lineWrapping: true, readOnly: true, mode: mode, theme: window.gitlist.getActualThemeCodemirror(), }); gitlist.viewer = cm; const isReallyFull = currentSizing === 'full' && !disableFull; if (isReallyFull) { setFull() } else { setScroll() } const scrollToEditor = () => { let line = location.hash.startsWith('#L') ? location.hash.substring(2) : undefined if (line !== undefined) { setTimeout(() => { line = parseInt(line) cm.setSelection({ line: line - 1 , char: 0, }, { line: line - 1, char: Number.MAX_SAFE_INTEGER }) cm.scrollIntoView({line: line, char:0}, isReallyFull ? window.innerHeight / 2 : 100) /* const codes = $('.CodeMirror-linenumber') for(let codeLinenumber of codes) { const $codeLinenumber = $(codeLinenumber) const findLine = $codeLinenumber.text(); if(findLine === line ) { break; } } */ }, 250) } } if (location.search.includes('edit=1')) { setTimeout(() => { $buttonEdit.click(); scrollToEditor(); }, 500) } else { scrollToEditor(); } } createCodeMirror(); const $showSvgButton = $('#p3x-gitlist-file-svg-show') if ($showSvgButton.length > 0) { const $svgElements = $('.p3x-gitlist-file-svg-toggle') const $cmWrapper = $(cm.getWrapperElement()) const $svgContentWrapper = $('#p3x-gitlist-file-svg-content') $showSvgButton.click(() => { if ($showSvgButton.hasClass('active')) { $svgContentWrapper.empty() } else { const image = `` // console.log(image) $svgContentWrapper.append(image) } $showSvgButton.toggleClass('active') $svgElements.toggle() $cmWrapper.toggle() }) } } }) src/browser/js/gitgraph.js/000077500000000000000000000000001516067322700161755ustar00rootroot00000000000000src/browser/js/gitgraph.js/LICENSE000066400000000000000000000027451516067322700172120ustar00rootroot00000000000000Copyright (c) 2011, Terrence Lee All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the fgdev nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.src/browser/js/gitgraph.js/README.md000066400000000000000000000040201516067322700174500ustar00rootroot00000000000000gitgraph.js ======= gitgraph.js a tool for converting "git log --graph" from pure ascii characters graph to a canvas image. Quick Start (with PHP demo) ----- 1. Get a copy of this repo (clone or [download tarball](http://github.com/bluef/gitgraph.js/tarball/master)) 2. Put everything in the directory where PHP file can be excuted 3. Edit example.php, change the value of GIT_REPO_PATH on line 2 to the path of your repo define("GIT_REPO_PATH", "/path/to/your/repo"); 4. Go visit example.php in your browser Miscellaneous ------------ jQuery is only for parsing DOM License ------- Copyright (c) 2011, Terrence Lee All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the fgdev nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.src/browser/js/gitgraph.js/gitgraph.css000066400000000000000000000011751516067322700205200ustar00rootroot00000000000000em {font-style:normal;} #git-graph-container, #rel-container {float:left;} #git-graph-container {} #git-graph-container li {list-style-type:none;height:20px;line-height:20px;overflow:hidden;} #git-graph-container li .node-relation {font-family:'Bitstream Vera Sans Mono', 'Courier', monospace;} #git-graph-container li .author {color:#666666;} #git-graph-container li .time {color:#999999;font-size:80%} #git-graph-container li a {color:#000000;} #git-graph-container li a em {color:#BB0000;border-bottom:1px dotted #BBBBBB;text-decoration:none;font-style:normal;} #rev-list {margin:0;padding:0 5px 0 0;} #graph-raw-list {margin:0px;}src/browser/js/gitgraph.js/gitgraph.js000066400000000000000000000331611516067322700203440ustar00rootroot00000000000000/** * This File is a part of the GitList Project at https://github.com/patrikx3/gitlist * * @license https://github.com/patrikx3/gitlist/blob/master/LICENSE * @author Patrik Laszlo https://github.com/patrikx3/gitlist */ /* * Copyright (c) 2011, Terrence Lee * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ global.gitGraph = function (canvas, rawGraphList, config) { if (!canvas.getContext) { return; } if (typeof config === "undefined") { config = { unitSize: 20, lineWidth: 3, nodeRadius: 4 }; } var flows = []; var graphList = []; var ctx = canvas.getContext("2d"); var devicePixelRatio = window.devicePixelRatio || 1; var backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; var ratio = devicePixelRatio / backingStoreRatio; var init = function () { var maxWidth = 0; var i; var l = rawGraphList.length; var row; var midStr; for (i = 0; i < l; i++) { midStr = rawGraphList[i].replace(/\s+/g, " ").replace(/^\s+|\s+$/g, ""); maxWidth = Math.max(midStr.replace(/(\_|\s)/g, "").length, maxWidth); row = midStr.split(""); graphList.unshift(row); } var width = maxWidth * config.unitSize; var height = graphList.length * config.unitSize; canvas.width = width * ratio; canvas.height = height * ratio; canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; ctx.fillStyle = window.gitlist.canvasDotColor; ctx.lineWidth = config.lineWidth; ctx.lineJoin = "round"; ctx.lineCap = "round"; ctx.scale(ratio, ratio); }; var genRandomStr = function () { var chars = "0123456789ABCDEF"; var stringLength = 6; var randomString = '', rnum, i; for (i = 0; i < stringLength; i++) { rnum = Math.floor(Math.random() * chars.length); randomString += chars.substring(rnum, rnum + 1); } return randomString; }; var findFlow = function (id) { var i = flows.length; while (i-- && flows[i].id !== id) { } return i; }; var findColomn = function (symbol, row) { var i = row.length; while (i-- && row[i] !== symbol) { } return i; }; var findBranchOut = function (row) { if (!row) { return -1 } var i = row.length; while (i-- && !(row[i - 1] && row[i] === "/" && row[i - 1] === "|") && !(row[i - 2] && row[i] === "_" && row[i - 2] === "|")) { } return i; } var genNewFlow = function () { var newId; do { newId = genRandomStr(); } while (findFlow(newId) !== -1); return {id: newId, color: window.gitlist.randomCanvasLaneColors() }; }; //draw method var drawLineRight = function (x, y, color) { ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo(x, y + config.unitSize / 2); ctx.lineTo(x + config.unitSize, y + config.unitSize / 2); ctx.stroke(); }; var drawLineUp = function (x, y, color) { ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo(x, y + config.unitSize / 2); ctx.lineTo(x, y - config.unitSize / 2); ctx.stroke(); }; var drawNode = function (x, y, color) { ctx.strokeStyle = color; drawLineUp(x, y, color); ctx.beginPath(); ctx.arc(x, y, config.nodeRadius, 0, Math.PI * 2, true); ctx.fill(); }; var drawLineIn = function (x, y, color) { ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo(x + config.unitSize, y + config.unitSize / 2); ctx.lineTo(x, y - config.unitSize / 2); ctx.stroke(); }; var drawLineOut = function (x, y, color) { ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo(x, y + config.unitSize / 2); ctx.lineTo(x + config.unitSize, y - config.unitSize / 2); ctx.stroke(); }; var draw = function (graphList) { var colomn, colomnIndex, prevColomn, condenseIndex; var x, y; var color; var nodePos, outPos; var tempFlow; var prevRowLength = 0; var flowSwapPos = -1; var lastLinePos; var i, k, l; var condenseCurrentLength, condensePrevLength = 0, condenseNextLength = 0; var inlineIntersect = false; //initiate for first row for (i = 0, l = graphList[0].length; i < l; i++) { if (graphList[0][i] !== "_" && graphList[0][i] !== " ") { flows.push(genNewFlow()); } } y = (canvas.height / ratio) - config.unitSize * 0.5; //iterate for (i = 0, l = graphList.length; i < l; i++) { x = config.unitSize * 0.5; var currentRow = graphList[i]; var nextRow = graphList[i + 1]; var prevRow = graphList[i - 1]; flowSwapPos = -1; condenseCurrentLength = currentRow.filter(function (val) { return (val !== " " && val !== "_") }).length; if (nextRow) { condenseNextLength = nextRow.filter(function (val) { return (val !== " " && val !== "_") }).length; } else { condenseNextLength = 0; } //pre process begin //use last row for analysing if (prevRow) { if (!inlineIntersect) { //intersect might happen for (colomnIndex = 0; colomnIndex < prevRowLength; colomnIndex++) { if (prevRow[colomnIndex + 1] && (prevRow[colomnIndex] === "/" && prevRow[colomnIndex + 1] === "|") || ((prevRow[colomnIndex] === "_" && prevRow[colomnIndex + 1] === "|") && (prevRow[colomnIndex + 2] === "/"))) { flowSwapPos = colomnIndex; //swap two flow tempFlow = {id: flows[flowSwapPos].id, color: flows[flowSwapPos].color}; flows[flowSwapPos].id = flows[flowSwapPos + 1].id; flows[flowSwapPos].color = flows[flowSwapPos + 1].color; flows[flowSwapPos + 1].id = tempFlow.id; flows[flowSwapPos + 1].color = tempFlow.color; } } } if (condensePrevLength < condenseCurrentLength && ((nodePos = findColomn("*", currentRow)) !== -1 && (findColomn("_", currentRow) === -1))) { flows.splice(nodePos - 1, 0, genNewFlow()); } if (prevRowLength > currentRow.length && (nodePos = findColomn("*", prevRow)) !== -1) { if (findColomn("_", currentRow) === -1 && findColomn("/", currentRow) === -1 && findColomn("\\", currentRow) === -1) { flows.splice(nodePos + 1, 1); } } } //done with the previous row prevRowLength = currentRow.length; //store for next round colomnIndex = 0; //reset index condenseIndex = 0; condensePrevLength = 0; while (colomnIndex < currentRow.length) { colomn = currentRow[colomnIndex]; if (colomn !== " " && colomn !== "_") { ++condensePrevLength; } if (colomn === " " && currentRow[colomnIndex + 1] && currentRow[colomnIndex + 1] === "_" && currentRow[colomnIndex - 1] && currentRow[colomnIndex - 1] === "|") { currentRow.splice(colomnIndex, 1); currentRow[colomnIndex] = "/"; colomn = "/"; } //create new flow only when no intersetc happened if (flowSwapPos === -1 && colomn === "/" && currentRow[colomnIndex - 1] && currentRow[colomnIndex - 1] === "|") { flows.splice(condenseIndex, 0, genNewFlow()); } //change \ and / to | when it's in the last position of the whole row if (colomn === "/" || colomn === "\\") { if (!(colomn === "/" && findBranchOut(nextRow) === -1)) { if ((lastLinePos = Math.max(findColomn("|", currentRow), findColomn("*", currentRow))) !== -1 && (lastLinePos < colomnIndex - 1)) { while (currentRow[++lastLinePos] === " ") { } if (lastLinePos === colomnIndex) { currentRow[colomnIndex] = "|"; } } } } if (colomn === "*" && prevRow && prevRow[condenseIndex + 1] === "\\") { flows.splice(condenseIndex + 1, 1); } if (colomn !== " ") { ++condenseIndex; } ++colomnIndex; } condenseCurrentLength = currentRow.filter(function (val) { return (val !== " " && val !== "_") }).length; //do some clean up if (flows.length > condenseCurrentLength) { flows.splice(condenseCurrentLength, flows.length - condenseCurrentLength); } colomnIndex = 0; //a little inline analysis and draw process while (colomnIndex < currentRow.length) { colomn = currentRow[colomnIndex]; prevColomn = currentRow[colomnIndex - 1]; if (currentRow[colomnIndex] === " ") { currentRow.splice(colomnIndex, 1); x += config.unitSize; continue; } //inline interset if ((colomn === "_" || colomn === "/") && currentRow[colomnIndex - 1] === "|" && currentRow[colomnIndex - 2] === "_") { inlineIntersect = true; tempFlow = flows.splice(colomnIndex - 2, 1)[0]; flows.splice(colomnIndex - 1, 0, tempFlow); currentRow.splice(colomnIndex - 2, 1); colomnIndex = colomnIndex - 1; } else { inlineIntersect = false; } color = flows[colomnIndex].color; switch (colomn) { case "_" : drawLineRight(x, y, color); x += config.unitSize; break; case "*" : drawNode(x, y, color); break; case "|" : drawLineUp(x, y, color); break; case "/" : if (prevColomn && (prevColomn === "/" || prevColomn === " ")) { x -= config.unitSize; } drawLineOut(x, y, color); x += config.unitSize; break; case "\\" : drawLineIn(x, y, color); break; } ++colomnIndex; } y -= config.unitSize; } }; init(); draw(graphList); };src/browser/js/global.js000066400000000000000000000041441516067322700155560ustar00rootroot00000000000000require('./global/cookie') require('./global/hash') require('./global/scroll') require('./global/path') require('./global/ajax') require('./global/git') require('./global/input') require('./global/snackbar') require('./global/theme') $(function () { const Cookies = require('js-cookie') currentTheme = window.gitlist.getActualTheme(window.gitlist.loadTheme) $('.dropdown-toggle').dropdown(); $('[data-toggle="tooltip"]').tooltip() window.gitlist.$body = $('body'); const es = document.getElementsByTagName('a') for(let i=0; i  ' + snack , timeout: window.gitlist.snapckbarLongTimeout, }) } const cookieShownChangelogName = 'p3x-gitlist-changelog-shown'; const cookieShownChangelog = Cookies.get(cookieShownChangelogName) if (!cookieShownChangelog) { Cookies.set(cookieShownChangelogName, true, window.gitlist.cookieSettings) window.gitlist.changeLog() } }); src/browser/js/global/000077500000000000000000000000001516067322700152155ustar00rootroot00000000000000src/browser/js/global/ajax.js000066400000000000000000000007521516067322700165020ustar00rootroot00000000000000window.gitlist.ajaxErrorHandler = (e) => { if (e.hasOwnProperty('status') && e.status !== 200 && typeof e.status !== 'string') { $.snackbar({ htmlAllowed: true, content: e.statusText, timeout: window.gitlist.snapckbarLongTimeout, }) } else { $.snackbar({ htmlAllowed: true, content: e.message, timeout: window.gitlist.snapckbarLongTimeout, }) } console.error(e); } src/browser/js/global/cookie.js000066400000000000000000000001611516067322700170220ustar00rootroot00000000000000window.gitlist.cookieSettings = { path: gitlist.basepath === '' ? '/' : gitlist.basepath, expires: 3650 }src/browser/js/global/git.js000066400000000000000000000100371516067322700163370ustar00rootroot00000000000000const Cookies = require('js-cookie') window.gitlist.gitNewPush = (json) => { if (typeof json === 'object' && json.hasOwnProperty('last-commit')) { const newLocation = `${window.gitlist.basepath}/${window.gitlist.repo}/commit/${json['last-commit']}?snack=` + encodeURIComponent(`The new push is executed. You are switched to the page where you can see the last commit.`) + `&delete-branch=${window.gitlist.branch}` // console.log(json, newLocation) location = newLocation return true; } return false; } window.gitlist.changeableCommit = () => { if (!window.gitlist.branches.includes(window.gitlist.branch)) { let branchInfo; if (window.gitlist.branches.length === 1) { branchInfo = `Only the ${window.gitlist.branches.join(', ')} branch is changeable.` } else { branchInfo = `Only the ${window.gitlist.branches.join(', ')} branches are changeable.` } $.snackbar({ htmlAllowed: true, content: `This commit ${window.gitlist.branch} is not changeable.
${branchInfo} `, timeout: window.gitlist.snapckbarLongTimeout, }) return false; } { return true } } window.gitlist.preloadCommitValues = (options) => { const { type } = options const inputs = { name: $(`#p3x-gitlist-modal-${ type }-name`), email: $(`#p3x-gitlist-modal-${ type }-email`), comment: $(`#p3x-gitlist-modal-${ type }-comment`), } for(let inputKey in inputs) { const input = inputs[inputKey] //console.log(inputKey, commentCookie) let cookieName = `p3x-gitlist-commit-${inputKey}`; if (inputKey === 'comment') { cookieName = `p3x-gitlist-commit-${type }-${inputKey}`; } const cookie = Cookies.get(cookieName) if (cookie) { input.val(cookie.trim()); } input.change(() => { const val = input.val().trim(); Cookies.set(cookieName, val, window.gitlist.cookieSettings); input.val(val); }) } window.gitlist.commitModelInputs[type] = inputs } window.gitlist.gitHelperAjax = async (options) => { const { modal, action, inputs, upload, fileUpload } = options let { data, filename } = options if (filename === undefined) { filename = window.gitlist.getPath(); } const defaultData = { email: inputs.email.val(), name: inputs.name.val(), comment: inputs.comment.val(), } if (data !== undefined) { data = Object.assign(defaultData, data) } else { data = defaultData } data.filename = filename modal.modal('hide') const url = `${window.gitlist.basepath}/${window.gitlist.repo}/git-helper/${window.gitlist.branch}/${action}` let request; if (upload === true) { const uploadData = new FormData(); for(let dataKey of Object.keys(data)) { uploadData.append(dataKey, data[dataKey]); } // console.log(fileUpload) uploadData.append('upload-file', fileUpload[0].files[0], data.filename ); request = $.ajax({ url: url, type: 'POST', data: uploadData, processData: false, contentType: false, }) } else { request = $.ajax({ url: url, type: 'POST', data: data, }) } const response = await request; const json = JSON.parse(response) if (json.error === true) { window.gitlist.ajaxErrorHandler(json); } if (json.hasOwnProperty('outputs') && json.outputs.length > 1 && typeof json.outputs[json.outputs.length - 1] === 'string') { const message = json.outputs[json.outputs.length - 1].trim(); if (message !== '') { $.snackbar({ htmlAllowed: true, content: message, timeout: window.gitlist.snapckbarLongTimeout, }) } } return json; }src/browser/js/global/hash.js000066400000000000000000000013261516067322700165000ustar00rootroot00000000000000 window.gitlist.pushHash = (hash) => { if(history.pushState) { const pushState = location.pathname + hash; history.pushState(null, null, pushState); } else { location.hash = hash; } }; window.gitlist.scrollHash = function(element, event) { const url = new URL(element.href) const id = url.hash.substring(1) const elfind = document.getElementById(id + '-parent') const elfind2 = document.getElementById(id) if (elfind === null && elfind2 === null) { return true; } window.gitlist.scrollIntoView(elfind || elfind2); if (event !== undefined) { event.preventDefault() window.gitlist.pushHash(url.hash) } return false; } src/browser/js/global/input.js000066400000000000000000000006131516067322700167120ustar00rootroot00000000000000const Cookies = require('js-cookie') window.gitlist.clearInput = (name) => { const input = $(`[name=${name}]`) input.val('') Cookies.set(`p3x-gitlist-${name}`, '', window.gitlist.cookieSettings) input.focus() } window.gitlist.setInputQuery = (name) => { const input = $(`[name=${name}]`) Cookies.set(`p3x-gitlist-${name}`, input.val(), window.gitlist.cookieSettings) } src/browser/js/global/location.js000066400000000000000000000000001516067322700173510ustar00rootroot00000000000000src/browser/js/global/path.js000066400000000000000000000006741516067322700165160ustar00rootroot00000000000000window.gitlist.getPaths = () => { const currentUrl = new URL(window.location) if (window.gitlist.basepath !== '') { currentUrl.pathname = currentUrl.pathname.substring(window.gitlist.basepath.length) } let paths = currentUrl.pathname.split('/'); return paths; } window.gitlist.getPath = () => { let paths = window.gitlist.getPaths(); paths = paths.slice(4); let path = paths.join('/') return path } src/browser/js/global/scroll.js000066400000000000000000000005671516067322700170610ustar00rootroot00000000000000window.gitlist.scrollIntoViewOptions = { behavior: "instant", // block: "start", // inline: "start" }; window.gitlist.scrollIntoView = (el) => { el.scrollIntoView(window.gitlist.scrollIntoViewOptions) /* if ((window.innerHeight + window.scrollY) <= document.body.offsetHeight - navbarHeight ) { window.scrollBy(0, -navbarHeight ) } */ };src/browser/js/global/snackbar.js000066400000000000000000000001771516067322700173440ustar00rootroot00000000000000 window.gitlist.invalidSnackbarCommit = () => { $.snackbar({ content: 'The commit form data is invalid..' }) } src/browser/js/global/theme.js000066400000000000000000000061271516067322700166630ustar00rootroot00000000000000 window.gitlist.isDark =(theme = window.gitlist.getActualTheme()) => { for(let i = 0; i < window.gitlist.dark.length; i++ ) { if (window.gitlist.dark[i] === theme) { return true; } } return false; } window.gitlist.getActualTheme = (theme = window.gitlist.getThemeCookie()) => { const actualTheme = theme.split('-')[1] return actualTheme; } window.gitlist.getActualThemeCodemirror = () => { if (window.gitlist.isDark(window.gitlist.getActualTheme())) { return window.gitlist.codemirrorTheme.dark; } else { return window.gitlist.codemirrorTheme.light; } } let currentTheme; let setTimeoutSwitch; window.gitlist.setTheme = () => { if (window.gitlist.$body === undefined) { setTimeout(() => { window.gitlist.setTheme() }) return; } const theme = window.gitlist.getActualTheme(); // console.log('theming', 'currenTheme', currentTheme, 'new theme', theme); if (theme === currentTheme) { // console.log('same theme') return; } const diffButtons = $('.p3x-gitlist-diff-button.active'); //console.log(diffButtons) for(let diffButton of diffButtons ) { diffButton.click(); } const switchThemeActually = () => { currentTheme = theme; /* const themeFragmentFileCodeMirror = (options) => { const { theme } = options for(let cm of window.gitlist.fragmentFileCodeMirror) { cm.setOption("theme", theme); } } */ let bodyAddClass let bodyRemoveClass let codeMirrorTheme if (window.gitlist.isDark(theme)) { bodyAddClass = 'p3x-gitlist-dark' bodyRemoveClass = 'p3x-gitlist-light' codeMirrorTheme = window.gitlist.codemirrorTheme.dark } else { bodyAddClass = 'p3x-gitlist-light' bodyRemoveClass = 'p3x-gitlist-dark' codeMirrorTheme = window.gitlist.codemirrorTheme.light } window.gitlist.$body.addClass(bodyAddClass) window.gitlist.$body.removeClass(bodyRemoveClass) if (gitlist.viewer !== undefined) { gitlist.viewer.setOption("theme", codeMirrorTheme); } if (gitlist.fragmentFileCodeMirror !== undefined) { gitlist.fragmentFileCodeMirror.setOption("theme", codeMirrorTheme); } window.gitlist.networkRedraw(); window.gitlist.treegraph(); // if (window.gitlist.lastloadSpan !== undefined && window.gitlist.lastloadSpan > 1000) { clearTimeout(setTimeoutSwitch) setTimeoutSwitch = setTimeout(() => { $('.p3x-gitlist-overlay').remove(); }, 250) } // console.log('p3x-gitlist switching theme') // } if (diffButtons.length > 0) { $.snackbar({ content: `We hid the shown diffs, to make the theme switching faster.`, timeout: window.gitlist.snapckbarLongTimeout, }); window.scrollTo(0, 0); setTimeout(switchThemeActually, 250) } else { switchThemeActually() } }src/browser/js/index.js000066400000000000000000000040211516067322700154170ustar00rootroot00000000000000$(function() { const List = require('list.js') const id = 'p3x-gitlist-index' if ($(`#${id}`).length) { const Cookies = require('js-cookie'); const cookieName = 'p3x-gitlist-query-repo'; const value = Cookies.get(cookieName) const input = $('[name="query-repo"]') const inputClear = $('#p3x-gitlist-index-list-clear'); const moment = require('moment') const times = $('.p3x-gitlist-index-repo-last-commit > .p3x-gitlist-index-repo-last-commit-time') const timesContainer = $('.p3x-gitlist-index-repo-last-commit') const timesContainerEmpty = $('.p3x-gitlist-index-repo-last-commit-empty') for(let timeindex in times) { const time = times[timeindex] if (String(time.innerText).trim() === '') { $(timesContainer[timeindex]).hide(); $(timesContainerEmpty[timeindex]).show(); } else { const timeMoment = moment(time.innerText).fromNow() time.innerText = timeMoment } } input.keypress(() => { Cookies.set(cookieName, input.val(), window.gitlist.cookieSettings) }) input.change(() => { Cookies.set(cookieName, input.val(), window.gitlist.cookieSettings) }) input.keydown(() => { Cookies.set(cookieName, input.val(), window.gitlist.cookieSettings) }) input.keydown(() => { Cookies.set(cookieName, input.val(), window.gitlist.cookieSettings) }) input.val(value); const listOptions = { valueNames: ['p3x-gitlist-index-name', 'p3x-gitlist-index-description', 'p3x-gitlist-index-repo-last-commit'], indexAsync: true, }; const list = new List(id, listOptions); if (value !== undefined) { list.search(value); } inputClear.click(() => { Cookies.remove(cookieName); input.val(''); list.search('') }) } }) src/browser/js/markdown.js000066400000000000000000000132201516067322700161330ustar00rootroot00000000000000const hljs = require('highlight.js/lib/highlight.js'); hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml.js')); hljs.registerLanguage('css', require('highlight.js/lib/languages/css.js')); hljs.registerLanguage('cmake', require('highlight.js/lib/languages/cmake.js')); hljs.registerLanguage('dockerfile', require('highlight.js/lib/languages/dockerfile.js')); hljs.registerLanguage('Dockerfile', require('highlight.js/lib/languages/dockerfile.js')); hljs.registerLanguage('less', require('highlight.js/lib/languages/less.js')); hljs.registerLanguage('scss', require('highlight.js/lib/languages/scss.js')); hljs.registerLanguage('yaml', require('highlight.js/lib/languages/yaml.js')); hljs.registerLanguage('powershell', require('highlight.js/lib/languages/powershell.js')); hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript.js')); hljs.registerLanguage('js', require('highlight.js/lib/languages/javascript.js')); hljs.registerLanguage('json', require('highlight.js/lib/languages/json.js')); hljs.registerLanguage('bash', require('highlight.js/lib/languages/shell.js')); hljs.registerLanguage('php', require('highlight.js/lib/languages/php.js')); hljs.registerLanguage('shell', require('highlight.js/lib/languages/shell.js')); hljs.registerLanguage('cmd', require('highlight.js/lib/languages/shell.js')); hljs.registerLanguage('typescript', require('highlight.js/lib/languages/typescript.js')); hljs.registerLanguage('ts', require('highlight.js/lib/languages/typescript.js')); const markdownRenderer = new marked.Renderer(); global.gitlist.markdownRenderer = markdownRenderer; const kebabCase = require('lodash/kebabCase') markdownRenderer.heading = function (text, level, raw) { level = level + 2; const ref = kebabCase(text).replace(/[^\x00-\xFF]/g, ""); const id = ref + '-parent'; const hover = ` onmouseenter="document.getElementById('${ref}').style.display = 'inline'" onmouseleave="document.getElementById('${ref}').style.display = 'none'" `; const element = `

${text} #
`; return element } markdownRenderer.link = function(href, title, text) { let a; if (href.startsWith('https:/') || href.startsWith('http:/')) { a = '' + text + ''; } else { const start = gitlist.basepath + '/' + gitlist.repo + '/blob/' + gitlist.branch + '/'; if (!location.pathname.startsWith(start)) { href = start + href; } else { const url = new URL(location); let path = url.pathname.split('/'); path.pop(); path = path.join('/'); href = path + '/' + href; } a = '' + text + ''; } return a; } markdownRenderer.image = function(href, title, text) { title = title || ''; text = text || ''; let resultText = title; if (text !== '') { if (title !== '') { resultText += ' - '; } resultText += text; } if (!href.startsWith('https:/') && !href.startsWith('http:/')) { const start = gitlist.basepath + '/' + gitlist.repo + '/raw/' + gitlist.branch + '/'; if (!location.pathname.startsWith(start)) { href = start + href; } else { const url = new URL(location); let path = url.pathname.split('/'); path.pop(); path = path.join('/'); href = path + '/' + href; } } const result = '' + htmlEncode(resultText) + ''; return result; }; markdownRenderer.code = (code, language ) => { if (language === undefined) { language = 'text'; } language = language.toLowerCase() if ((hljs.getLanguage(language) === 'undefined' || hljs.getLanguage(language) === undefined) && language !== 'text') { console.error(`Please add highlight.js as a language (could be a marked error as well, sometimes it thinks a language): ${language} We are not loading everything, since it is about 500kb`) } language = language === 'text' || language === undefined ? 'html' : language; const validLang = !!(language && hljs.getLanguage(language)); const highlighted = validLang ? hljs.highlight(language, code).value : code; return `
${highlighted}
`; }; markdownRenderer.codespan = (code) => { const lang = 'html'; const highlighted = hljs.highlight(lang, code).value ; return `${highlighted}`; } window.gitlist.markdownRenderer = markdownRenderer; $(function() { const mdContent = $('#p3x-gitlist-readme'); if (mdContent.length) { const twemojiSettings = require('./settings').twemoji; const html = marked(mdContent.text(), { renderer: markdownRenderer }); mdContent.html(twemoji.parse(html, twemojiSettings)); } }); window.gitlist.renderMarkdown = (options) => { const { markdown } = options; const twemojiSettings = require('./settings').twemoji; const markedHtml = marked(markdown, { renderer: window.gitlist.markdownRenderer }); const html = twemoji.parse(markedHtml, twemojiSettings) return html; }src/browser/js/menu-responsive.js000066400000000000000000000015721516067322700174570ustar00rootroot00000000000000module.exports = (options) => { const { menuList } = options; let { nav, navButton } = options if (nav === undefined) { nav = $('#p3x-gitlist-navigation') } if (navButton === undefined) { navButton = $('#p3x-gitlist-navigation-menu-button'); } const debounce = require('lodash/debounce') const debounceResize = debounce(() => { if (navButton.is(':visible')) { menuList.css({ 'maxHeight': 'auto', 'overflowX': 'visible', }); } else { const allowedMaxHeight = window.innerHeight - nav.height() - 20; menuList.css({ 'maxHeight': allowedMaxHeight, 'overflowX': 'auto' }); } }, 250); window.addEventListener('resize', debounceResize); debounceResize(); return debounceResize; }src/browser/js/network.js000066400000000000000000000446441516067322700160200ustar00rootroot00000000000000/** * Network Graph JS * This File is a part of the GitList Project at https://github.com/patrikx3/gitlist * * @license https://github.com/patrikx3/gitlist/blob/master/LICENSE * @author Lukas Domnick http://github.com/lukx * @author Patrik Laszlo https://github.com/patrikx3/gitlist */ // global config const cfg = { get laneColors() { if (window.gitlist.isDark()) { return ['#BDBDBD', '#BBDEFB', '#03A9F4', '#2196F3', '#BDBDBD', '#FFFFFF']; } else { return ['#455A64', '#607D8B', '#757575', '#9E9E9E', '#CFD8DC', '#BDBDBD']; } }, get dotColor() { if (window.gitlist.isDark()) { return '#ffffff88'; } else { return '#00000088'; } }, laneHeight: 20, columnWidth: 42, dotRadius: 8 }; Object.defineProperty(window.gitlist, 'canvasLaneColors', { get: () => { return cfg.laneColors; } }) Object.defineProperty(window.gitlist, 'canvasDotColor', { get: () => { return cfg.dotColor; } }) let nextLaneIndex = 0 window.gitlist.randomCanvasLaneColors = () => { const items = window.gitlist.canvasLaneColors; nextLaneIndex++ if (nextLaneIndex > items.length) { nextLaneIndex = 0 } return items[nextLaneIndex]; } const $ = require('jquery') const phpDate = require('php-date') /** * DragScrollr is a custom made x/y-Drag Scroll Plugin for Gitlist * * TODO: Make this touch-scrollable */ $.fn.dragScrollr = function () { let lastX, lastY, hotZone = 200, container = this.first(), domElement = container[0]; // so basically container without the jQuery stuff function handleMouseDown(evt) { container.on('mousemove', handleMouseMove); container.on('mouseup', handleMouseUp); container.on('mouseleave', handleMouseUp); lastX = evt.pageX; lastY = evt.pageY; } function handleMouseMove(evt) { evt.preventDefault(); // save the last scroll position to figure out whether the scroll event has entered the hot zone const lastScrollLeft = domElement.scrollLeft; domElement.scrollLeft = domElement.scrollLeft + lastX - evt.pageX; domElement.scrollTop = domElement.scrollTop + lastY - evt.pageY; if (lastScrollLeft > hotZone && domElement.scrollLeft <= hotZone) { container.trigger('enterHotZone'); } // when we move into the hot zone lastX = evt.pageX; lastY = evt.pageY; } function handleMouseUp(evt) { container.off('mousemove', handleMouseMove) .off('mouseup', handleMouseUp) .off('mouseleave', handleMouseUp); } // now bind the initial event container.on('mousedown', handleMouseDown); // return this instead of container, because of the .first() we applied - remember? return this; }; function graphLaneManager() { const that = {}, occupiedLanes = []; // "private" methods function findLaneNumberFor(commit) { if (commit.lane) { // oh? we've already got a lane? return commit.lane.number; } // find out which lane may draw our dot on. Start with a free one let laneNumber = findFreeLane(); // if the child is a merge, we need to figure out which lane we may render this commit on. // Rules are simple: A "parent" by the same author as the merge may render on the same line as the child // others take the next free lane. // furthermore, commits in a linear line of events may stay on the same lane, too if (commit.children.length > 0) { if (!commit.children[0].isMerge // linear ... || (commit.children[0].isMerge && commit.children[0].author.email === commit.author.email) // same author ) { laneNumber = commit.children[0].lane.number; } } return laneNumber; } function findFreeLane() { let i = 0; while (true) { // if an array index is not yet defined or set to false, the lane with that number is free. if (!occupiedLanes[i]) { return i; } i++; } } that.occupy = function (lane) { // make sure we work with lane numbers here if (typeof lane === 'object') { lane = lane.number; } occupiedLanes[lane] = true; }; that.free = function (lane) { // make sure we work with lane numbers here if (typeof lane === 'object') { lane = lane.number; } occupiedLanes[lane] = false; }; that.getLaneForCommit = function (commit) { // does this commit have a lane already? if (commit.lane) return commit.lane; const laneNumber = findLaneNumberFor(commit); return that.getLane(laneNumber); }; that.getLane = function (laneNumber) { return { 'number': laneNumber, 'centerY': (laneNumber * cfg.laneHeight) + (cfg.laneHeight / 2), 'color': cfg.laneColors[laneNumber % cfg.laneColors.length] }; }; return that; } function commitDetailOverlay() { var that = {}, el = $('
'), imageDisplay = $('').appendTo(el), messageDisplay = $('
').appendTo(el), metaDisplay = $('
').appendTo(el), authorDisplay = $('').appendTo(metaDisplay), dateDisplay = $('').appendTo(metaDisplay); el.hide(); /** * Pads an input number with one leading '0' if needed, and assure it's a string * * @param input Number * @returns String */ function twoDigits(input) { if (input < 10) { return '0' + input; } return '' + input; } /** * Transform a JS Native Date Object to a string, maintaining the same format given in the commit_list view * 'd/m/Y \\a\\t H:i:s' * * @param date Date * @returns String */ function getDateString(date) { return phpDate(gitlist.dateFormat, date) } /** * update the author view * * @param author */ function setAuthor(author) { authorDisplay.html(author.name) .attr('href', 'mailto:' + author.email); imageDisplay.attr('src', author.image); } /** * Set the commit that is being displayed in this detail overlay instance * * @param commit * @return that */ that.setCommit = function (commit) { setAuthor(commit.author); dateDisplay.html(' authored on ' + getDateString(commit.date)); messageDisplay.html(commit.message); return that; }; // expose some jquery functions that.show = function () { el.show(); return that; }; that.hide = function () { el.hide(); return that; }; that.appendTo = function (where) { el.appendTo(where); return that; }; that.positionTo = function (x, y) { el.css('left', x + 'px'); el.css('top', y + 'px'); }; that.outerWidth = function () { return el.outerWidth.apply(el, arguments); }; return that; } function commitDataRetriever(startPage, callback) { let that = {}, nextPage = startPage; let indicatorElements; global.isLoading = false; that.updateIndicators = function () { if (global.isLoading) { $(indicatorElements).addClass('loading-commits'); } else { $(indicatorElements).removeClass('loading-commits'); } }; that.bindIndicator = function (el) { if (!indicatorElements) { indicatorElements = $(el); } else { indicatorElements = indicatorElements.add(el); } }; that.unbindIndicator = function (el) { indicatorElements.not(el); }; function handleNetworkDataLoaded(data) { global.isLoading = false; that.updateIndicators(); nextPage = data.nextPage; if (!data.commits || data.commits.length === 0) { callback(null); } callback(data.commits); } function handleNetworkDataError() { throw "Network Data Error while retrieving Commits"; } that.retrieve = function () { if (!nextPage) { callback(null); return; } global.isLoading = true; that.updateIndicators(); $.ajax({ dataType: "json", url: nextPage, success: handleNetworkDataLoaded, error: handleNetworkDataError }); }; that.hasMore = function () { return (!!nextPage); }; return that; } window.gitlist.networkRedraw = () => { // initialise network graph only when there is one network graph container on the page if ($('div.network-graph').length !== 1) { return; } // the element into which we will render our graph let commitsGraph = $('div.network-graph').first(); commitsGraph.find('svg').remove(); commitsGraph.find('.network-commit-overlay').remove(); let laneManager = graphLaneManager() let dataRetriever = commitDataRetriever(commitsGraph.data('source'), handleCommitsRetrieved) let paper = Raphael(commitsGraph[0], commitsGraph.width(), commitsGraph.height()) let usedColumns = 0 let detailOverlay = commitDetailOverlay() dataRetriever.bindIndicator(commitsGraph.parent('.network-view')); detailOverlay.appendTo(commitsGraph); function handleEnterHotZone() { dataRetriever.retrieve(); } function handleCommitsRetrieved(commits) { // no commits or empty commits array? Well, we can't draw a graph of that if (commits === null) { handleNoAvailableData(); return; } prepareCommits(commits); renderCommits(commits); } function handleNoAvailableData() { window.console && console.log('No (more) Data available'); } const awaitedParents = {}; function prepareCommits(commits) { $.each(commits, function (index, commit) { prepareCommit(commit); }); } function prepareCommit(commit) { // make "date" an actual JS Date object commit.date = new Date(commit.date * 1000); // the parents will be filled once they have become prepared commit.parents = []; // we will want to store this commit's children commit.children = getChildrenFor(commit); commit.isFork = (commit.children.length > 1); commit.isMerge = (commit.parentsHash.length > 1); // after a fork, the occupied lanes must be cleaned up. The children used some lanes we no longer occupy if (commit.isFork === true) { $.each(commit.children, function (key, thisChild) { // free this lane laneManager.occupy(thisChild.lane); }); } commit.lane = laneManager.getLaneForCommit(commit); // now the lane we chose must be marked occupied again. laneManager.occupy(commit.lane); registerAwaitedParentsFor(commit); } /** * Add a new childCommit to the dictionary of awaited parents * * @param commit who is waiting? */ function registerAwaitedParentsFor(commit) { // This commit's parents are not yet known in our little world, as we are rendering following the time line. // Therefore we are registering this commit as "waiting" for each of the parent hashes $.each(commit.parentsHash, function (key, thisParentHash) { // If awaitedParents does not already have a key for thisParent's hash, initialise as array if (!awaitedParents.hasOwnProperty(thisParentHash)) { awaitedParents[thisParentHash] = [commit]; } else { awaitedParents[thisParentHash].push(commit); } }); } function getChildrenFor(commit) { let children = []; if (awaitedParents.hasOwnProperty(commit.hash)) { // there are child commits waiting children = awaitedParents[commit.hash]; // let the children know their parent objects $.each(children, function (key, thisChild) { thisChild.parents.push(commit); }); // remove this item from parentsBeingWaitedFor delete awaitedParents[commit.hash]; } return children; } const lastRenderedDate = new Date(0); function renderCommits(commits) { let neededWidth = ((usedColumns + Object.keys(commits).length) * cfg.columnWidth); if (neededWidth > paper.width) { extendPaper(neededWidth, paper.height); } else if (dataRetriever.hasMore()) { // this is the case when we have not loaded enough commits to fill the paper yet. Get some more then... dataRetriever.retrieve(); } $.each(commits, function (index, commit) { if (lastRenderedDate.getYear() !== commit.date.getYear() || lastRenderedDate.getMonth() !== commit.date.getMonth() || lastRenderedDate.getDate() !== commit.date.getDate()) { // TODO: If desired, one could add a time scale on top, maybe. } renderCommit(commit); }); } function renderCommit(commit) { // find the column this dot is drawn on usedColumns++; commit.column = usedColumns; commit.dot = paper.circle(getXPositionForColumnNumber(commit.column), commit.lane.centerY, cfg.dotRadius); commit.dot.attr({ fill: commit.lane.color, stroke: 'none', cursor: 'pointer' }) .data('commit', commit) .mouseover(handleCommitMouseover) .mouseout(handleCommitMouseout) .click(handleCommitClick); // maybe we have not enough space for the lane yet if (commit.lane.centerY + cfg.laneHeight > paper.height) { extendPaper(paper.width, commit.lane.centerY + cfg.laneHeight) } $.each(commit.children, function (idx, thisChild) { // if there is one child only, stay on the commit's lane as long as possible when connecting the dots. // but if there is more than one child, switch to the child's lane ASAP. // this is to display merges and forks where they happen (ie. at a commit node/ a dot), rather than // connecting from a line. // So: commit.isFork decides whether or not we must switch lanes early connectDots(commit, thisChild, commit.isFork); }); } /** * * @param firstCommit * @param secondCommit * @param switchLanesEarly (boolean): Move the line to the secondCommit's lane ASAP? Defaults to false */ function connectDots(firstCommit, secondCommit, switchLanesEarly) { // default value for switchLanesEarly switchLanesEarly = switchLanesEarly || false; const lineLane = switchLanesEarly ? secondCommit.lane : firstCommit.lane; // the connection has 4 stops, resulting in the following 3 segments: // - from the x/y center of firstCommit.dot to the rightmost end (x) of the commit's column, with y=lineLane // - from the rightmost end of firstCommit's column, to the leftmost end of secondCommit's column // - from the leftmost end of secondCommit's column (y=lineLane) to the x/y center of secondCommit paper.path( getSvgLineString( [firstCommit.dot.attr('cx'), firstCommit.dot.attr('cy')], [firstCommit.dot.attr('cx') + (cfg.columnWidth / 2), lineLane.centerY], [secondCommit.dot.attr('cx') - (cfg.columnWidth / 2), lineLane.centerY], [secondCommit.dot.attr('cx'), secondCommit.dot.attr('cy')] ) ).attr({"stroke": lineLane.color, "stroke-width": 2}).toBack(); } // set together a path string from any amount of arguments // each argument is an array of [x, y] within the paper's coordinate system function getSvgLineString() { if (arguments.length < 2) return; let svgString = 'M' + arguments[0][0] + ' ' + arguments[0][1]; for (let i = 1, j = arguments.length; i < j; i++) { svgString += 'L' + arguments[i][0] + ' ' + arguments[i][1]; } return svgString; } function handleCommitMouseover(evt) { detailOverlay.setCommit(this.data('commit')) .show(); let xPos = evt.pageX - commitsGraph.offset().left + commitsGraph.scrollLeft() - (detailOverlay.outerWidth() / 2); // check that x doesn't run out the viewport xPos = Math.max(xPos, commitsGraph.scrollLeft() + 10); xPos = Math.min(xPos, commitsGraph.scrollLeft() + commitsGraph.width() - detailOverlay.outerWidth() - 10); detailOverlay.positionTo(xPos, evt.pageY - commitsGraph.offset().top + commitsGraph.scrollTop() + 10); } function handleCommitMouseout(evt) { detailOverlay.hide(); } function handleCommitClick(evt) { location.href = this.data('commit').details; } function getXPositionForColumnNumber(columnNumber) { // we want the column's center point return (paper.width - (columnNumber * cfg.columnWidth) + (cfg.columnWidth / 2)); } function extendPaper(newWidth, newHeight) { const deltaX = newWidth - paper.width; paper.setSize(newWidth, newHeight); // fixup parent's scroll position try { paper.canvas.parentNode.scrollLeft = paper.canvas.parentNode.scrollLeft + deltaX; } catch (e) { console.warn(e) } // now fixup the x positions of existing circles and lines paper.forEach(function (el) { if (el.type === "circle") { el.attr('cx', el.attr('cx') + deltaX); } else if (el.type === "path") { let newXTranslation = el.data('currentXTranslation') || 0; newXTranslation += deltaX; el.transform('t' + newXTranslation + ' 0'); el.data('currentXTranslation', newXTranslation); } }); } commitsGraph.dragScrollr(); commitsGraph.on('enterHotZone', handleEnterHotZone); // load initial data dataRetriever.retrieve(); } src/browser/js/paginate.js000066400000000000000000000036631516067322700161130ustar00rootroot00000000000000$(function() { let $pager; let loading = false; let $button; let noMore = false; let $noCommits; const nextCommitListItem = () => { if (loading === true) { return; } loading = true; const href = $button.attr('href'); //console.log(href); if (href === undefined) { loading = false; if (!noMore) { $.snackbar({ htmlAllowed: true, content: `No more commits.` }); noMore = true; } return } noMore = false; const retrieve = `${location.pathname}${href}` history.pushState({ }, document.title, retrieve); const url = new URL(location); url.search = href; url.searchParams.append('ajax', 1) $.ajax({ url: url.toString(), async: true, type: "GET", }).then(function(html) { $pager.after(html); $pager.remove(); loading = false; window.gitlist.constructCommitsListConstructMarkdown() paginate(); $('.p3x-gitlist-commit-list-no-more-commit').remove(); }); } function paginate() { $pager = $('#p3x-gitlist-pager-bottom'); $noCommits = $('#p3x-gitlist-commits-no-more') if ($noCommits.length !== 0) { $pager.remove(); return; } $button = $pager.find('#p3x-gitlist-commit-list-next'); $button.one('click', function (e) { e.preventDefault(); nextCommitListItem() return false; }); } paginate(); if ($button.length > 0) { $(window).scroll(function () { if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) { nextCommitListItem(); } }); } }) src/browser/js/settings.js000066400000000000000000000004621516067322700161550ustar00rootroot00000000000000module.exports = { twemoji: { callback: function(icon, options, variant) { if (icon === "") { return false; } return ''.concat(options.base, options.size, '/', icon, options.ext); }, folder: 'svg', ext: '.svg', } }src/browser/js/theme-switcher.js000066400000000000000000000051541516067322700172500ustar00rootroot00000000000000const themes = require('./themes.js') window.gitlist.themes = themes; const Cookies = require('js-cookie') const themeCookieName = 'gitlist-bootstrap-theme' function getThemeCookie() { const theme = Cookies.get(themeCookieName) return theme || 'bootstrap-cosmo'; } gitlist.getThemeCookie = getThemeCookie; $(function () { const themeList = $('#theme-list'); const menuResponsive = require('./menu-responsive') const debounceResize = menuResponsive({ menuList: themeList, }) function setThemeCookie(theme) { Cookies.set(themeCookieName, theme, window.gitlist.cookieSettings); } const currentCookie = getThemeCookie('gitlist-bootstrap-theme'); const darkMenu = []; const lightMenu = [] for (let key in themes) { const actualTheme = key.substring(10) const menu = '
  • ' + actualTheme + '
  • '; if (window.gitlist.isDark(actualTheme)) { darkMenu.push(menu) } else { lightMenu.push(menu) } } for (let menu of lightMenu) { themeList.append(menu); } themeList.append('
  • ') for (let menu of darkMenu) { themeList.append(menu); } const themesheet = $('#bootstrap-theme'); let deferredSwitchTheme; $('.theme-link').click(function (event) { event.preventDefault(); const generateNewTheme = () => { debounceResize(); themeList.find('.active').removeClass('active'); const $this = $(this); $this.parent().addClass('active'); const themeurl = themes[$this.attr('data-theme')]; setThemeCookie($this.attr('data-theme')); const href = themeurl; const currentHref = themesheet.attr('href') // console.log('currentHref', currentHref, 'href', href) if (currentHref === href) { return; } $('body').prepend(`

    Hang on, we are not reloading the server ...
    `) // console.log('p3x-gitlist themer swtich') themesheet.attr('href', href); gitlist.setTheme() } clearTimeout(deferredSwitchTheme) deferredSwitchTheme = setTimeout(() => { generateNewTheme() }, 250) }); }); src/browser/js/themes.js000066400000000000000000000043211516067322700156000ustar00rootroot00000000000000 const themes = { "bootstrap-default": "prod/css/bootstrap-default.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-solar": "prod/css/bootstrap-solar.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-cerulean": "prod/css/bootstrap-cerulean.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-cosmo": "prod/css/bootstrap-cosmo.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-cyborg": "prod/css/bootstrap-cyborg.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-darkly": "prod/css/bootstrap-darkly.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-flatly": "prod/css/bootstrap-flatly.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-journal": "prod/css/bootstrap-journal.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-lumen": "prod/css/bootstrap-lumen.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-paper": "prod/css/bootstrap-paper.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-readable": "prod/css/bootstrap-readable.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-sandstone": "prod/css/bootstrap-sandstone.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-simplex": "prod/css/bootstrap-simplex.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-slate": "prod/css/bootstrap-slate.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-spacelab": "prod/css/bootstrap-spacelab.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-superhero": "prod/css/bootstrap-superhero.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-united": "prod/css/bootstrap-united.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css", "bootstrap-yeti": "prod/css/bootstrap-yeti.3e55dab4dd79dbd686ea6b62c7a722e13ba6bf6522943149ebfd651d3dfe50de.css" }; module.exports = themes; src/browser/js/todo.js000066400000000000000000000011541516067322700152610ustar00rootroot00000000000000let $todoModal let todoHtml; window.gitlist.todo = async() => { if (todoHtml === undefined) { try { const response = await $.ajax('https://raw.githubusercontent.com/patrikx3/gitlist/master/todo.md') const $todoModalBody = $('#p3x-gitlist-modal-todo-body') todoHtml = window.gitlist.renderMarkdown({ markdown: response }) $todoModalBody.html(todoHtml); } catch(e) { window.gitlist.ajaxErrorHandler(e) } } $todoModal.modal('show') } $(async () => { $todoModal = $('#p3x-gitlist-modal-todo') }) src/browser/js/tree.js000066400000000000000000000112251516067322700152530ustar00rootroot00000000000000$(() => { const $buttonNewFile = $('#p3x-gitlist-tree-new-file') if ($buttonNewFile.length === 0) { return } const path = window.gitlist.getPath() // const $buttonNewFileModal = $('#p3x-gitlist-modal-new') const $formNewfile = $('#p3x-gitlist-modal-new-form') //const $buttonSubmitNewfile = $('#p3x-gitlist-modal-new-filename-confirm') const $inputNewfile = $('#p3x-gitlist-modal-new-filename') $inputNewfile.val(path) $buttonNewFile.click(() => { if (!window.gitlist.changeableCommit()) { return } $buttonNewFileModal.modal('show') }) $formNewfile[0].addEventListener('submit', async(ev) => { ev.preventDefault(); if($formNewfile[0].checkValidity() === false) { window.gitlist.invalidSnackbarCommit() return; } try { const inputs = window.gitlist.commitModelInputs['new'] const json = await window.gitlist.gitHelperAjax({ modal: $buttonNewFileModal, action: 'new-file-or-directory', inputs: inputs, filename: $inputNewfile.val(), }) if (window.gitlist.gitNewPush(json)) { return } } catch(e) { window.gitlist.ajaxErrorHandler(e) } return false; // $buttonNewFileModal.modal('hide') }, false) // // const $buttonNewBinary = $('#p3x-gitlist-tree-new-binary') const $buttonNewBinaryModal = $('#p3x-gitlist-modal-new-binary') const $formNewfileBinary = $('#p3x-gitlist-modal-new-binary-form') //const $buttonSubmitNewfileBinary = $('#p3x-gitlist-modal-new-filename-binary-confirm') const $inputNewfileBinaryFile = $('#p3x-gitlist-modal-new-binary-filename-binary') const $inputNewfileBinaryUpload = $('#p3x-gitlist-modal-new-binary-filename-binary-upload') const $inputNewfileBinaryOverride = $('#p3x-gitlist-modal-new-binary-filename-binary-override') $inputNewfileBinaryFile.val(path) let uploadBinaryFilename = '' $buttonNewBinary.click(() => { if (!window.gitlist.changeableCommit()) { return } $buttonNewBinaryModal.modal('show') }) $inputNewfileBinaryUpload.change(() => { if ($inputNewfileBinaryUpload[0].files.length === 0) { $inputNewfileBinaryFile.val(`${path}`) } else { uploadBinaryFilename = $inputNewfileBinaryUpload[0].files[0].name $inputNewfileBinaryFile.val(`${path}${uploadBinaryFilename}`) } }) $formNewfileBinary[0].addEventListener('submit', async function(ev) { ev.preventDefault(); if($formNewfileBinary[0].checkValidity() === false) { window.gitlist.invalidSnackbarCommit() return; } try { // http://php.net/manual/en/features.file-upload.php#114004 const inputs = window.gitlist.commitModelInputs['new-binary'] const json = await window.gitlist.gitHelperAjax({ upload: true, modal: $buttonNewBinaryModal, action: 'file-binary', inputs: inputs, filename: $inputNewfileBinaryFile.val(), fileUpload: $inputNewfileBinaryUpload, data: { override: $inputNewfileBinaryOverride.is(`:checked`) ? 1 : 0, } }) if (window.gitlist.gitNewPush(json)) { return } } catch(e) { window.gitlist.ajaxErrorHandler(e) } return false; }, false); /* $buttonSubmitNewfileBinary.click(async () => { if($formNewfileBinary[0].checkValidity() === false) { window.gitlist.invalidSnackbarCommit() return; } try { const inputs = window.gitlist.commitModelInputs['new-binary'] const json = await window.gitlist.gitHelperAjax({ upload: true, modal: $buttonNewBinaryModal, action: 'file-binary', inputs: inputs, filename: $inputNewfileBinaryFile.val(), fileUpload: $inputNewfileBinaryUpload, data: { override: $inputNewfileBinaryOverride.val() } }) if (window.gitlist.gitNewPush(json)) { return } } catch(e) { alert(e); window.gitlist.ajaxErrorHandler(e) } return false; }) */ // }) src/browser/js/treegraph.js000066400000000000000000000020261516067322700162740ustar00rootroot00000000000000$(() => { const subjects = $('.p3x-gitlist-treegraph-subject') if (subjects) { for (let subject of subjects) { const html = window.gitlist.renderMarkdown({ markdown: subject.innerHTML }) subject.innerHTML = html // console.log(html) } } }) window.gitlist.treegraph = () => { if (!document.getElementById('graph-canvas')) { return; } const log = $("#p3x-gitlist-treegraph-log"); if (log) { const graphList = []; $("#graph-raw-list li span.node-relation").each(function () { graphList.push($(this).text()); }) const $li = $('#rev-list li'); $li.each(function() { const $this = $(this) const text = $this.find('.p3x-gitlist-treegraph-subject').text() if (text !== undefined && text !== '') { $this.attr('title', text) } }) global.gitGraph(document.getElementById('graph-canvas'), graphList); } } src/browser/layout.tpl.twig000066400000000000000000000031301516067322700163450ustar00rootroot00000000000000 {% block title %}Welcome!{% endblock %} {% block body %}{% endblock %} {% block javascripts %} {% endblock %} src/browser/less/000077500000000000000000000000001516067322700143075ustar00rootroot00000000000000src/browser/less/blame.less000066400000000000000000000000611516067322700162540ustar00rootroot00000000000000/* they are using the file-fragment.less file */src/browser/less/browser.less000066400000000000000000000015661516067322700166720ustar00rootroot00000000000000#p3x-gitlist-branch-list { overflow: hidden; white-space:nowrap; text-overflow:ellipsis; max-width:200px; display:inline-block; @media only screen and (max-width : 768px) { max-width: 100%; width: 100%; } } .p3x-gitlist-branch-menu-header, .p3x-gitlist-branch-menu-item { padding: @padding-base-vertical; } #p3x-gitlist-branch-list-container { .p3x-gitlist-branch-menu-item, a { text-decoration: none; color: @dropdown-link-color; } .p3x-gitlist-branch-menu-item:hover { cursor: pointer; } .p3x-gitlist-branch-menu-item:hover, .p3x-gitlist-branch-menu-item:hover a { background-color: @dropdown-link-hover-bg; color: @dropdown-link-hover-color; } .p3x-gitlist-branch-menu-item.active, .p3x-gitlist-branch-menu-item.active a { background-color: @dropdown-link-active-bg; color: @dropdown-link-active-color; } } src/browser/less/clone-button.less000066400000000000000000000000741516067322700176110ustar00rootroot00000000000000#p3x-gitlist-modal-clone { input { color: black; } }src/browser/less/codemirror.less000066400000000000000000000002611516067322700173430ustar00rootroot00000000000000 .p3x-gitlist-light { .CodeMirror { border: 1px solid @gray-lighter; } } .p3x-gitlist-dark { .CodeMirror { border: 1px solid @navbar-default-bg !important; } } src/browser/less/commit.less000066400000000000000000000033041516067322700164670ustar00rootroot00000000000000.p3x-gitlist-diff-container { width: 100%; display: block; padding: 0; a, a:hover { text-decoration: none !important; } .p3x-gitlist-diff { max-width: 100%; min-width: 100%; overflow-x: auto; pre { background-color: @body-bg; color: @text-color; margin: 0; padding: 0 0 0 6px; border-radius: 0; overflow: hidden; text-overflow: ellipsis; border: 1px solid transparent; &:hover { // filter: brightness(0.9); } } table { font-family: monospace; } table td { padding: 0; } .new { background-color: rgba(@alert-success-bg, 0.2); border-bottom: 1px solid @alert-success-bg !important; // color: @alert-success-text; } .old { background-color: rgba(@alert-danger-bg, 0.2); border-bottom: 1px solid @alert-danger-bg !important; // color: @alert-danger-text; } .chunk { background-color: @alert-info-bg; color: @alert-info-text; } .lineNo { // background-color: @navbar-default-bg !important; // color: @navbar-default-color !important; padding: 0 6px; text-align: right; border-right: 1px solid @navbar-default-border !important; } .lineNo a { color: @navbar-default-color !important; } } .image-blob { padding: 10px; max-width: 100%; } } #p3x-gitlist-commit-heading { font-size: 120%; overflow: hidden !important; text-overflow: ellipsis !important; } .p3x-gitlist-commit-diff-loader { opacity: 0.5; padding: 20px; font-size: 125%; } /* .p3x-gitlist-commit-diff-loader-bottom { border-bottom: @list-group-border solid 1px; } */src/browser/less/commits-lists.less000066400000000000000000000001741516067322700200100ustar00rootroot00000000000000.p3x-gitlist-commits-list { max-width: 100% !important; td { display: inline-block; word-wrap: break-word; } }src/browser/less/default.less000066400000000000000000000052401516067322700166240ustar00rootroot00000000000000@import "../../../node_modules/snackbarjs/src/snackbar"; @import "../../../node_modules/snackbarjs/themes-less/material"; #snackbar-container { left: auto !important; right: 20px !important; } @media (max-width: 767px) { #snackbar-container { left: 0px !important; right: 0px !important; width: 100%; .snackbar { min-width: 100%; } [class="snackbar snackbar-opened"] ~ .snackbar.toast { margin-top: 20px; } [class="snackbar snackbar-opened"] { border-radius: 0; margin-bottom: 0; } } } .p3x-gitlist-button { .btn { margin-bottom: @padding-base-vertical !important; } } .p3x-gitlist-light { .CodeMirror { border: 1px solid @gray-lighter; } } .p3x-gitlist-dark { .CodeMirror { border: 1px solid @navbar-default-bg !important; } } code, pre { overflow-x: auto; //white-space:pre-wrap; font-family: monospace; } .p3x-gitlist-dark { #snackbar-container > div { border: 1px solid @table-bg-accent; } code.p3x-gitlist-code, pre.p3x-gitlist-code { background-color: lighten(@navbar-inverse-bg, 5%); color: @navbar-inverse-color; border-color: @navbar-inverse-border; } } .p3x-gitlist-markdown-heading-container { position: relative; } a.p3x-gitlist-markdown-heading-link { display: none; font-size: inherit; text-decoration: none !important; } .p3x-gitlist-markdown-image { max-width: 100%; } .p3x-gitlist-breadcrumb-divider { min-height: @padding-base-vertical; display: block; clear: both;; } img.emoji { height: 1em; width: 1em; margin: 0 .05em 0 .1em; vertical-align: -0.1em; } .p3x-list-item-header { font-weight: bold !important; } .breadcrumb { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .p3x-gitlist-max-width { max-width: 100%; max-height: auto; } .modal { text-align: left; } .media-body { text-overflow: ellipsis; } .modal-dialog { ::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ color: @state-danger-text ; } input:invalid { color: @state-danger-text; background-color: @state-danger-bg; border-color: @state-danger-border; } } .btn:hover svg.svg-inline--fa { transition-duration: 250ms; transition-property: transform; transform: scale(1.1) rotate(360deg); } @import "index"; @import "clone-button.less"; @import "footer"; @import "menu"; @import "treegraph"; @import "file"; @import "markdown"; @import "browser"; @import "overlay"; @import "codemirror"; @import "commit"; @import "tree"; @import "navigation"; @import "blame"; @import "search"; @import "file-fragment"; @import "commits-lists"; @import "list-group-striped"; @import "pager"; @import "network";src/browser/less/file-fragment.less000066400000000000000000000006571516067322700177270ustar00rootroot00000000000000.p3x-gitlilst-file-fragment-heading-right { float: right; } .p3x-gitlist-file-fragment-panel { .panel-body { padding: 0 !important; .p3x-gitlist-file-fragment-text { padding: @panel-body-padding !important; } .CodeMirror-wrap { border: none !important; } } } .p3x-gitlist-file-fragment-text { overflow-x: auto; white-space: pre; margin: 0; padding-bottom: 0; padding-top: 0; }src/browser/less/file.less000066400000000000000000000001461516067322700161170ustar00rootroot00000000000000#p3x-gitlist-file-codemirror-exceeded { display: none; margin-bottom: @padding-base-horizontal; } src/browser/less/footer.less000066400000000000000000000002521516067322700164740ustar00rootroot00000000000000 #p3x-gitlist-footer { margin-top: @padding-base-vertical; text-align: right; } .p3x-gitlist-header-height { height: @navbar-height + (@navbar-height * 0.5); } src/browser/less/index.less000066400000000000000000000003171516067322700163070ustar00rootroot00000000000000#p3x-gitlist-index { .p3x-gitlist-index-description { // padding-left: @padding-base-vertical; padding-right: @padding-base-vertical; } .p3x-gitlist-index-reponame { padding-left: 0px; } }src/browser/less/list-group-striped.less000066400000000000000000000003641516067322700207570ustar00rootroot00000000000000ul.list-group.list-group-striped li:nth-of-type(odd){ } ul.list-group.list-group-striped li:nth-of-type(even){ background-color: fadein(@table-bg-accent, 50%); } ul.list-group.list-group-hover li:hover{ background-color: @table-bg-hover ; }src/browser/less/markdown.less000066400000000000000000000006131516067322700170210ustar00rootroot00000000000000.p3x-gitlist-light { @import (less) '../../../node_modules/highlight.js/styles/github.css'; } .p3x-gitlist-dark { @import (less) '../../../node_modules/highlight.js/styles/dracula.css'; } #p3x-gitlist-readme, .p3x-gitlist-readme { pre { padding: 0; } } .p3x-gitlist-markdown-clear-fix { pre, code { padding: 0; border: none; margin: 0; text-align: left; } }src/browser/less/menu.less000066400000000000000000000004041516067322700161410ustar00rootroot00000000000000#p3x-gitlist-main-tabs * { margin-bottom: 2px; position: relative; a:hover > svg.svg-inline--fa { transition-duration: 250ms; transition-property: transform; transform: scale(1.2) rotate(360deg); } svg.svg-inline--fa { top: 2px; } }src/browser/less/navigation.less000066400000000000000000000010421516067322700173330ustar00rootroot00000000000000.p3x-gitlist-navigation-brand-icon { } #p3x-gitlist-navigation-brand-icon-pure { height: @navbar-height - 16px; width: auto; top: 8px; position: absolute; } #p3x-gitlist-navigation-brand-icon { height: @navbar-height - @navbar-margin-bottom - 2px; width: auto; display: inline-block; } #p3x-gitlist-navigation-brand-icon-title { display: inline-block; margin-left: 36px; } #p3x-gitlist-navigation-menu-button:hover { transition-duration: 250ms; transition-property: transform; transform: scale(1.1) rotate(360deg); }src/browser/less/network.less000066400000000000000000000023261516067322700166730ustar00rootroot00000000000000.network-view { width: 100%; margin-bottom: @line-height-base; position: relative; overflow: hidden; &.loading-commits { &:before { content: ""; display: block; height: 100px; width: 200px; box-sizing: border-box; padding: 10px; font-weight: bold; position: absolute; left: 50%; top: 50%; margin-left: -100px; margin-top: -50px; z-index: 2000; } .network-header .meta:after { content: " - Loading"; } } .network-header { } pre { margin: 0; padding: 12px; border: none; } .network-graph { height: 400px; overflow: hidden; cursor: move; position: relative; border: 1px solid @table-border-color; .network-commit-overlay { position: absolute; width: 400px; top: 0; left: 0; padding: 8px; margin: 0 0 @line-height-base; list-style: none; background-color: @navbar-default-bg; color: @navbar-default-color; a, a:active { background-color: @navbar-default-bg; color: @navbar-inverse-color; } img { float: left; margin-right: 10px; } div { } } } }src/browser/less/overlay.less000066400000000000000000000006271516067322700166650ustar00rootroot00000000000000.p3x-gitlist-overlay { i { font-size: 400% !important; } z-index: 999999; font-size: 125%; position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; text-align:center; /*Flexbox*/ display: flex; flex-direction: column; align-items: center; align-content: center; justify-content: center; background-color: rgba(0, 0, 0, 0.9); color: rgba(255, 255, 255, 0.5); } src/browser/less/pager.less000066400000000000000000000002451516067322700162760ustar00rootroot00000000000000#p3x-gitlist-pager-bottom { clear: both; width: 100%; } #p3x-gitlist-commit-list-next { float: right; } #p3x-gitlist-commit-list-previous { float: left; }src/browser/less/search.less000066400000000000000000000000611516067322700164410ustar00rootroot00000000000000/* they are using the file-fragment.less file */src/browser/less/style.less000066400000000000000000000001341516067322700163350ustar00rootroot00000000000000// Init Bootstrap @import "../../node_modules/bootstrap/less/variables"; @import "default"; src/browser/less/theme/000077500000000000000000000000001516067322700154115ustar00rootroot00000000000000src/browser/less/theme/cerulean.less000066400000000000000000000004361516067322700201020ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/cerulean/variables"; @import "../../../../node_modules/bootswatch/cerulean/bootswatch"; @import "../default"; src/browser/less/theme/cosmo.less000066400000000000000000000004301516067322700174160ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/cosmo/variables"; @import "../../../../node_modules/bootswatch/cosmo/bootswatch"; @import "../default"; src/browser/less/theme/cyborg.less000066400000000000000000000004321516067322700175650ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/cyborg/variables"; @import "../../../../node_modules/bootswatch/cyborg/bootswatch"; @import "../default"; src/browser/less/theme/darkly.less000066400000000000000000000004321516067322700175660ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/darkly/variables"; @import "../../../../node_modules/bootswatch/darkly/bootswatch"; @import "../default"; src/browser/less/theme/default.less000066400000000000000000000001241516067322700177220ustar00rootroot00000000000000 @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../default"; src/browser/less/theme/flatly.less000066400000000000000000000004321516067322700175730ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/flatly/variables"; @import "../../../../node_modules/bootswatch/flatly/bootswatch"; @import "../default"; src/browser/less/theme/journal.less000066400000000000000000000004341516067322700177540ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/journal/variables"; @import "../../../../node_modules/bootswatch/journal/bootswatch"; @import "../default"; src/browser/less/theme/lumen.less000066400000000000000000000004301516067322700174160ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/lumen/variables"; @import "../../../../node_modules/bootswatch/lumen/bootswatch"; @import "../default"; src/browser/less/theme/paper.less000066400000000000000000000004301516067322700174050ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/paper/variables"; @import "../../../../node_modules/bootswatch/paper/bootswatch"; @import "../default"; src/browser/less/theme/readable.less000066400000000000000000000004361516067322700200430ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/readable/variables"; @import "../../../../node_modules/bootswatch/readable/bootswatch"; @import "../default"; src/browser/less/theme/sandstone.less000066400000000000000000000004401516067322700202750ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/sandstone/variables"; @import "../../../../node_modules/bootswatch/sandstone/bootswatch"; @import "../default"; src/browser/less/theme/simplex.less000066400000000000000000000004341516067322700177630ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/simplex/variables"; @import "../../../../node_modules/bootswatch/simplex/bootswatch"; @import "../default"; src/browser/less/theme/slate.less000066400000000000000000000004301516067322700174060ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "../../../../node_modules/bootswatch/slate/variables"; @import "../../../../node_modules/bootswatch/slate/bootswatch"; @import "../default"; src/browser/less/theme/solar.less000066400000000000000000000003201516067322700174140ustar00rootroot00000000000000 @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700'; @import "../../../../node_modules/bootstrap/less/bootstrap"; @import "solar/variables"; @import "solar/bootswatch"; @import "../default"; src/browser/less/theme/solar/000077500000000000000000000000001516067322700165315ustar00rootroot00000000000000src/browser/less/theme/solar/bootswatch.less000066400000000000000000000050261516067322700216010ustar00rootroot00000000000000// Solar 3.3.7 // Bootswatch // ----------------------------------------------------- @web-font-path: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700"; .web-font(@path) { @import url("@{path}"); } .web-font(@web-font-path); // Navbar ===================================================================== // Buttons ==================================================================== // Typography ================================================================= .text-primary, .text-primary:hover { color: @brand-primary; } .text-success, .text-success:hover { color: @brand-success; } .text-danger, .text-danger:hover { color: @brand-danger; } .text-warning, .text-warning:hover { color: @brand-warning; } .text-info, .text-info:hover { color: @brand-info; } .bg-success, .bg-info, .bg-warning, .bg-danger { color: #fff; } // Tables ===================================================================== table, .table { .success, .info, .warning, .danger { td, th { color: #fff; } } } // Forms ====================================================================== .has-warning { .help-block, .control-label, .radio, .checkbox, .radio-inline, .checkbox-inline, &.radio label, &.checkbox label, &.radio-inline label, &.checkbox-inline label, .form-control-feedback { color: @brand-warning; } .form-control, .form-control:focus, .input-group-addon { border-color: @brand-warning; } } .has-error { .help-block, .control-label, .radio, .checkbox, .radio-inline, .checkbox-inline, &.radio label, &.checkbox label, &.radio-inline label, &.checkbox-inline label, .form-control-feedback { color: @brand-danger; } .form-control, .form-control:focus, .input-group-addon { border-color: @brand-danger; } } .has-success { .help-block, .control-label, .radio, .checkbox, .radio-inline, .checkbox-inline, &.radio label, &.checkbox label, &.radio-inline label, &.checkbox-inline label, .form-control-feedback { color: @brand-success; } .form-control, .form-control:focus, .input-group-addon { border-color: @brand-success; } } .input-group-addon { color: @text-color; } // Navs ======================================================================= // Indicators ================================================================= // Progress bars ============================================================== // Containers =================================================================src/browser/less/theme/solar/variables.less000066400000000000000000000661751516067322700214100ustar00rootroot00000000000000// Solar 3.3.7 // Variables // -------------------------------------------------- //== Colors // //## Gray and brand colors for use across Bootstrap. @gray-base: #000; @gray-darker: #002B36; @gray-dark: #073642; @gray: #839496; @gray-light: #EEE8D5; @gray-lighter: #FDF6E3; @brand-primary: #B58900; @brand-success: #2AA198; @brand-info: #268BD2; @brand-warning: #CB4B16; @brand-danger: #D33682; //== Scaffolding // //## Settings for some of the most global styles. //** Background color for ``. @body-bg: @gray-darker; //** Global text color on ``. @text-color: @gray; //** Global textual link color. @link-color: @brand-success; //** Link hover color set via `darken()` function. @link-hover-color: @link-color; //** Link hover decoration. @link-hover-decoration: underline; //== Typography // //## Font, line-height, and color for body text, headings, and more. @font-family-sans-serif: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; @font-family-serif: Georgia, "Times New Roman", Times, serif; //** Default monospace fonts for ``, ``, and `
    `.
    @font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
    @font-family-base:        @font-family-sans-serif;
    
    @font-size-base:          14px;
    @font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
    @font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
    
    @font-size-h1:            floor((@font-size-base * 2.6)); // ~36px
    @font-size-h2:            floor((@font-size-base * 2.15)); // ~30px
    @font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px
    @font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px
    @font-size-h5:            @font-size-base;
    @font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px
    
    //** Unit-less `line-height` for use in components like buttons.
    @line-height-base:        1.428571429; // 20/14
    //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
    @line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
    
    //** By default, this inherits from the ``.
    @headings-font-family:    inherit;
    @headings-font-weight:    500;
    @headings-line-height:    1.1;
    @headings-color:          inherit;
    
    
    //== Iconography
    //
    //## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
    
    //** Load fonts from this directory.
    @icon-font-path:          "../fonts/";
    //** File name for all font files.
    @icon-font-name:          "glyphicons-halflings-regular";
    //** Element ID within SVG icon file.
    @icon-font-svg-id:        "glyphicons_halflingsregular";
    
    
    //== Components
    //
    //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
    
    @padding-base-vertical:     6px;
    @padding-base-horizontal:   12px;
    
    @padding-large-vertical:    10px;
    @padding-large-horizontal:  16px;
    
    @padding-small-vertical:    5px;
    @padding-small-horizontal:  10px;
    
    @padding-xs-vertical:       1px;
    @padding-xs-horizontal:     5px;
    
    @line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome
    @line-height-small:         1.5;
    
    @border-radius-base:        4px;
    @border-radius-large:       6px;
    @border-radius-small:       3px;
    
    //** Global color for active items (e.g., navs or dropdowns).
    @component-active-color:    #fff;
    //** Global background color for active items (e.g., navs or dropdowns).
    @component-active-bg:       @gray-dark;
    
    //** Width of the `border` for generating carets that indicate dropdowns.
    @caret-width-base:          4px;
    //** Carets increase slightly in size for larger components.
    @caret-width-large:         5px;
    
    
    //== Tables
    //
    //## Customizes the `.table` component with basic values, each used across all table variations.
    
    //** Padding for ``s and ``s.
    @table-cell-padding:            8px;
    //** Padding for cells in `.table-condensed`.
    @table-condensed-cell-padding:  5px;
    
    //** Default background color used for all tables.
    @table-bg:                      transparent;
    //** Background color used for `.table-striped`.
    @table-bg-accent:               @gray-dark;
    //** Background color used for `.table-hover`.
    @table-bg-hover:                rgba(255,255,255,.075);
    @table-bg-active:               @table-bg-hover;
    
    //** Border color for table and cell borders.
    @table-border-color:            @component-active-bg;
    
    
    //== Buttons
    //
    //## For each of Bootstrap's buttons, define text, background and border color.
    
    @btn-font-weight:                normal;
    
    @btn-default-color:              #fff;
    @btn-default-bg:                 #586E75;
    @btn-default-border:             @btn-default-bg;
    
    @btn-primary-color:              #fff;
    @btn-primary-bg:                 @brand-primary;
    @btn-primary-border:             @btn-primary-bg;
    
    @btn-success-color:              #fff;
    @btn-success-bg:                 @brand-success;
    @btn-success-border:             @btn-success-bg;
    
    @btn-info-color:                 #fff;
    @btn-info-bg:                    @brand-info;
    @btn-info-border:                @btn-info-bg;
    
    @btn-warning-color:              #fff;
    @btn-warning-bg:                 @brand-warning;
    @btn-warning-border:             @btn-warning-bg;
    
    @btn-danger-color:               #fff;
    @btn-danger-bg:                  @brand-danger;
    @btn-danger-border:              @btn-danger-bg;
    
    @btn-link-disabled-color:        @gray-light;
    
    // Allows for customizing button radius independently from global border radius
    @btn-border-radius-base:         @border-radius-base;
    @btn-border-radius-large:        @border-radius-large;
    @btn-border-radius-small:        @border-radius-small;
    
    
    //== Forms
    //
    //##
    
    //** `` background color
    @input-bg:                       #A9BDBD;
    //** `` background color
    @input-bg-disabled:              #657B83;
    
    //** Text color for ``s
    @input-color:                    @gray-dark;
    //** `` border color
    @input-border:                   rgba(0, 0, 0, .15);
    
    // TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
    //** Default `.form-control` border radius
    // This has no effect on ``s in CSS.
    @input-border-radius:            @border-radius-base;
    //** Large `.form-control` border radius
    @input-border-radius-large:      @border-radius-large;
    //** Small `.form-control` border radius
    @input-border-radius-small:      @border-radius-small;
    
    //** Border color for inputs on focus
    @input-border-focus:             #66afe9;
    
    //** Placeholder text color
    @input-color-placeholder:        #657B83;
    
    //** Default `.form-control` height
    @input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
    //** Large `.form-control` height
    @input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
    //** Small `.form-control` height
    @input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
    
    //** `.form-group` margin
    @form-group-margin-bottom:       15px;
    
    @legend-color:                   @text-color;
    @legend-border-color:            @component-active-bg;
    
    //** Background color for textual input addons
    @input-group-addon-bg:           @gray-dark;
    //** Border color for textual input addons
    @input-group-addon-border-color: @input-border;
    
    //** Disabled cursor for form controls and buttons.
    @cursor-disabled:                not-allowed;
    
    
    //== Dropdowns
    //
    //## Dropdown menu container and contents.
    
    //** Background for the dropdown menu.
    @dropdown-bg:                    @gray-dark;
    //** Dropdown menu `border-color`.
    @dropdown-border:                rgba(0,0,0,.15);
    //** Dropdown menu `border-color` **for IE8**.
    @dropdown-fallback-border:       #ccc;
    //** Divider color for between dropdown items.
    @dropdown-divider-bg:            @body-bg;
    
    //** Dropdown link text color.
    @dropdown-link-color:            @text-color;
    //** Hover color for dropdown links.
    @dropdown-link-hover-color:      rgba(255,255,255,.75);
    //** Hover background for dropdown links.
    @dropdown-link-hover-bg:         @body-bg;
    
    //** Active dropdown menu item text color.
    @dropdown-link-active-color:     @component-active-color;
    //** Active dropdown menu item background color.
    @dropdown-link-active-bg:        @component-active-bg;
    
    //** Disabled dropdown menu item background color.
    @dropdown-link-disabled-color:   @gray-light;
    
    //** Text color for headers within dropdown menus.
    @dropdown-header-color:          @gray-light;
    
    //** Deprecated `@dropdown-caret-color` as of v3.1.0
    @dropdown-caret-color:           #000;
    
    
    //-- Z-index master list
    //
    // Warning: Avoid customizing these values. They're used for a bird's eye view
    // of components dependent on the z-axis and are designed to all work together.
    //
    // Note: These variables are not generated into the Customizer.
    
    @zindex-navbar:            1000;
    @zindex-dropdown:          1000;
    @zindex-popover:           1060;
    @zindex-tooltip:           1070;
    @zindex-navbar-fixed:      1030;
    @zindex-modal-background:  1040;
    @zindex-modal:             1050;
    
    
    //== Media queries breakpoints
    //
    //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
    
    // Extra small screen / phone
    //** Deprecated `@screen-xs` as of v3.0.1
    @screen-xs:                  480px;
    //** Deprecated `@screen-xs-min` as of v3.2.0
    @screen-xs-min:              @screen-xs;
    //** Deprecated `@screen-phone` as of v3.0.1
    @screen-phone:               @screen-xs-min;
    
    // Small screen / tablet
    //** Deprecated `@screen-sm` as of v3.0.1
    @screen-sm:                  768px;
    @screen-sm-min:              @screen-sm;
    //** Deprecated `@screen-tablet` as of v3.0.1
    @screen-tablet:              @screen-sm-min;
    
    // Medium screen / desktop
    //** Deprecated `@screen-md` as of v3.0.1
    @screen-md:                  992px;
    @screen-md-min:              @screen-md;
    //** Deprecated `@screen-desktop` as of v3.0.1
    @screen-desktop:             @screen-md-min;
    
    // Large screen / wide desktop
    //** Deprecated `@screen-lg` as of v3.0.1
    @screen-lg:                  1200px;
    @screen-lg-min:              @screen-lg;
    //** Deprecated `@screen-lg-desktop` as of v3.0.1
    @screen-lg-desktop:          @screen-lg-min;
    
    // So media queries don't overlap when required, provide a maximum
    @screen-xs-max:              (@screen-sm-min - 1);
    @screen-sm-max:              (@screen-md-min - 1);
    @screen-md-max:              (@screen-lg-min - 1);
    
    
    //== Grid system
    //
    //## Define your custom responsive grid.
    
    //** Number of columns in the grid.
    @grid-columns:              12;
    //** Padding between columns. Gets divided in half for the left and right.
    @grid-gutter-width:         30px;
    // Navbar collapse
    //** Point at which the navbar becomes uncollapsed.
    @grid-float-breakpoint:     @screen-sm-min;
    //** Point at which the navbar begins collapsing.
    @grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
    
    
    //== Container sizes
    //
    //## Define the maximum width of `.container` for different screen sizes.
    
    // Small screen / tablet
    @container-tablet:             (720px + @grid-gutter-width);
    //** For `@screen-sm-min` and up.
    @container-sm:                 @container-tablet;
    
    // Medium screen / desktop
    @container-desktop:            (940px + @grid-gutter-width);
    //** For `@screen-md-min` and up.
    @container-md:                 @container-desktop;
    
    // Large screen / wide desktop
    @container-large-desktop:      (1140px + @grid-gutter-width);
    //** For `@screen-lg-min` and up.
    @container-lg:                 @container-large-desktop;
    
    
    //== Navbar
    //
    //##
    
    // Basics of a navbar
    @navbar-height:                    50px;
    @navbar-margin-bottom:             @line-height-computed;
    @navbar-border-radius:             @border-radius-base;
    @navbar-padding-horizontal:        floor((@grid-gutter-width / 2));
    @navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
    @navbar-collapse-max-height:       340px;
    
    @navbar-default-color:             @text-color;
    @navbar-default-bg:                @gray-dark;
    @navbar-default-border:            transparent;
    
    // Navbar links
    @navbar-default-link-color:                rgba(255,255,255,.5);
    @navbar-default-link-hover-color:          rgba(255,255,255,.75);
    @navbar-default-link-hover-bg:             transparent;
    @navbar-default-link-active-color:         #fff;
    @navbar-default-link-active-bg:            darken(@navbar-default-bg, 2.5%);
    @navbar-default-link-disabled-color:       rgba(255,255,255,.25);
    @navbar-default-link-disabled-bg:          transparent;
    
    // Navbar brand label
    @navbar-default-brand-color:               #fff;
    @navbar-default-brand-hover-color:         #fff;
    @navbar-default-brand-hover-bg:            transparent;
    
    // Navbar toggle
    @navbar-default-toggle-hover-bg:           @navbar-default-link-color;
    @navbar-default-toggle-icon-bar-bg:        @navbar-default-toggle-hover-bg;
    @navbar-default-toggle-border-color:       @navbar-default-link-disabled-color;
    
    
    //=== Inverted navbar
    // Reset inverted navbar basics
    @navbar-inverse-color:                      lighten(@gray-light, 15%);
    @navbar-inverse-bg:                         @brand-primary;
    @navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
    
    // Inverted navbar links
    @navbar-inverse-link-color:                 @navbar-default-link-color;
    @navbar-inverse-link-hover-color:           @navbar-default-link-hover-color;
    @navbar-inverse-link-hover-bg:              transparent;
    @navbar-inverse-link-active-color:          #fff;
    @navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 10%);
    @navbar-inverse-link-disabled-color:        @navbar-default-link-disabled-color;
    @navbar-inverse-link-disabled-bg:           transparent;
    
    // Inverted navbar brand label
    @navbar-inverse-brand-color:                #fff;
    @navbar-inverse-brand-hover-color:          #fff;
    @navbar-inverse-brand-hover-bg:             transparent;
    
    // Inverted navbar toggle
    @navbar-inverse-toggle-hover-bg:            @navbar-inverse-link-color;
    @navbar-inverse-toggle-icon-bar-bg:         @navbar-inverse-toggle-hover-bg;
    @navbar-inverse-toggle-border-color:        @navbar-inverse-link-disabled-color;
    
    
    //== Navs
    //
    //##
    
    //=== Shared nav styles
    @nav-link-padding:                          10px 15px;
    @nav-link-hover-bg:                         transparent;
    
    @nav-disabled-link-color:                   @gray;
    @nav-disabled-link-hover-color:             @gray;
    
    //== Tabs
    @nav-tabs-border-color:                     @gray-dark;
    
    @nav-tabs-link-hover-border-color:          @nav-tabs-border-color;
    
    @nav-tabs-active-link-hover-bg:             @body-bg;
    @nav-tabs-active-link-hover-color:          #fff;
    @nav-tabs-active-link-hover-border-color:   @nav-tabs-border-color;
    
    @nav-tabs-justified-link-border-color:            @nav-tabs-border-color;
    @nav-tabs-justified-active-link-border-color:     @body-bg;
    
    //== Pills
    @nav-pills-border-radius:                   @border-radius-base;
    @nav-pills-active-link-hover-bg:            @component-active-bg;
    @nav-pills-active-link-hover-color:         @component-active-color;
    
    
    //== Pagination
    //
    //##
    
    @pagination-color:                     @link-color;
    @pagination-bg:                        transparent;
    @pagination-border:                    @component-active-bg;
    
    @pagination-hover-color:               @link-hover-color;
    @pagination-hover-bg:                  @component-active-bg;
    @pagination-hover-border:              @component-active-bg;
    
    @pagination-active-color:              rgba(255,255,255,.75);
    @pagination-active-bg:                 @component-active-bg;
    @pagination-active-border:             @component-active-bg;
    
    @pagination-disabled-color:            @component-active-bg;
    @pagination-disabled-bg:               transparent;
    @pagination-disabled-border:           @component-active-bg;
    
    
    //== Pager
    //
    //##
    
    @pager-bg:                             @pagination-bg;
    @pager-border:                         @pagination-border;
    @pager-border-radius:                  15px;
    
    @pager-hover-bg:                       @pagination-hover-bg;
    
    @pager-active-bg:                      @pagination-active-bg;
    @pager-active-color:                   @pagination-active-color;
    
    @pager-disabled-color:                 @pagination-disabled-color;
    
    
    //== Jumbotron
    //
    //##
    
    @jumbotron-padding:              30px;
    @jumbotron-color:                inherit;
    @jumbotron-bg:                   @component-active-bg;
    @jumbotron-heading-color:        inherit;
    @jumbotron-font-size:            ceil((@font-size-base * 1.5));
    @jumbotron-heading-font-size:    ceil((@font-size-base * 4.5));
    
    
    //== Form states and alerts
    //
    //## Define colors for form feedback states and, by default, alerts.
    
    @state-success-text:             #fff;
    @state-success-bg:               @brand-success;
    @state-success-border:           @state-success-bg;
    
    @state-info-text:                #fff;
    @state-info-bg:                  @brand-info;
    @state-info-border:              @state-info-bg;
    
    @state-warning-text:             #fff;
    @state-warning-bg:               @brand-warning;
    @state-warning-border:           @state-warning-bg;
    
    @state-danger-text:              #fff;
    @state-danger-bg:                @brand-danger;
    @state-danger-border:            @state-danger-bg;
    
    
    //== Tooltips
    //
    //##
    
    //** Tooltip max width
    @tooltip-max-width:           200px;
    //** Tooltip text color
    @tooltip-color:               #fff;
    //** Tooltip background color
    @tooltip-bg:                  #000;
    @tooltip-opacity:             .9;
    
    //** Tooltip arrow width
    @tooltip-arrow-width:         5px;
    //** Tooltip arrow color
    @tooltip-arrow-color:         @tooltip-bg;
    
    
    //== Popovers
    //
    //##
    
    //** Popover body background color
    @popover-bg:                          @component-active-bg;
    //** Popover maximum width
    @popover-max-width:                   276px;
    //** Popover border color
    @popover-border-color:                @body-bg;
    //** Popover fallback border color
    @popover-fallback-border-color:       #ccc;
    
    //** Popover title background color
    @popover-title-bg:                    @component-active-bg;
    
    //** Popover arrow width
    @popover-arrow-width:                 10px;
    //** Popover arrow color
    @popover-arrow-color:                 @popover-bg;
    
    //** Popover outer arrow width
    @popover-arrow-outer-width:           (@popover-arrow-width + 1);
    //** Popover outer arrow color
    @popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
    //** Popover outer arrow fallback color
    @popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
    
    
    //== Labels
    //
    //##
    
    //** Default label background color
    @label-default-bg:            @btn-default-bg;
    //** Primary label background color
    @label-primary-bg:            @brand-primary;
    //** Success label background color
    @label-success-bg:            @brand-success;
    //** Info label background color
    @label-info-bg:               @brand-info;
    //** Warning label background color
    @label-warning-bg:            @brand-warning;
    //** Danger label background color
    @label-danger-bg:             @brand-danger;
    
    //** Default label text color
    @label-color:                 #fff;
    //** Default text color of a linked label
    @label-link-hover-color:      #fff;
    
    
    //== Modals
    //
    //##
    
    //** Padding applied to the modal body
    @modal-inner-padding:         15px;
    
    //** Padding applied to the modal title
    @modal-title-padding:         15px;
    //** Modal title line-height
    @modal-title-line-height:     @line-height-base;
    
    //** Background color of modal content area
    @modal-content-bg:                             @component-active-bg;
    //** Modal content border color
    @modal-content-border-color:                   @body-bg;
    //** Modal content border color **for IE8**
    @modal-content-fallback-border-color:          #999;
    
    //** Modal backdrop background color
    @modal-backdrop-bg:           #000;
    //** Modal backdrop opacity
    @modal-backdrop-opacity:      .5;
    //** Modal header border color
    @modal-header-border-color:   @modal-content-border-color;
    //** Modal footer border color
    @modal-footer-border-color:   @modal-header-border-color;
    
    @modal-lg:                    900px;
    @modal-md:                    600px;
    @modal-sm:                    300px;
    
    
    //== Alerts
    //
    //## Define alert colors, border radius, and padding.
    
    @alert-padding:               15px;
    @alert-border-radius:         @border-radius-base;
    @alert-link-font-weight:      bold;
    
    @alert-success-bg:            @state-success-bg;
    @alert-success-text:          @state-success-text;
    @alert-success-border:        @state-success-border;
    
    @alert-info-bg:               @state-info-bg;
    @alert-info-text:             @state-info-text;
    @alert-info-border:           @state-info-border;
    
    @alert-warning-bg:            @state-warning-bg;
    @alert-warning-text:          @state-warning-text;
    @alert-warning-border:        @state-warning-border;
    
    @alert-danger-bg:             @state-danger-bg;
    @alert-danger-text:           @state-danger-text;
    @alert-danger-border:         @state-danger-border;
    
    
    //== Progress bars
    //
    //##
    
    //** Background color of the whole progress component
    @progress-bg:                 @component-active-bg;
    //** Progress bar text color
    @progress-bar-color:          #fff;
    //** Variable for setting rounded corners on progress bar.
    @progress-border-radius:      @border-radius-base;
    
    //** Default progress bar color
    @progress-bar-bg:             @brand-primary;
    //** Success progress bar color
    @progress-bar-success-bg:     @brand-success;
    //** Warning progress bar color
    @progress-bar-warning-bg:     @brand-warning;
    //** Danger progress bar color
    @progress-bar-danger-bg:      @brand-danger;
    //** Info progress bar color
    @progress-bar-info-bg:        @brand-info;
    
    
    //== List group
    //
    //##
    
    //** Background color on `.list-group-item`
    @list-group-bg:                 transparent;
    //** `.list-group-item` border color
    @list-group-border:             @component-active-bg;
    //** List group border radius
    @list-group-border-radius:      @border-radius-base;
    
    //** Background color of single list items on hover
    @list-group-hover-bg:           @component-active-bg;
    //** Text color of active list items
    @list-group-active-color:       @component-active-color;
    //** Background color of active list items
    @list-group-active-bg:          @component-active-bg;
    //** Border color of active list elements
    @list-group-active-border:      @list-group-active-bg;
    //** Text color for content within active list items
    @list-group-active-text-color:  @component-active-color;
    
    //** Text color of disabled list items
    @list-group-disabled-color:      @component-active-bg;
    //** Background color of disabled list items
    @list-group-disabled-bg:         transparent;
    //** Text color for content within disabled list items
    @list-group-disabled-text-color: @list-group-disabled-color;
    
    @list-group-link-color:         @text-color;
    @list-group-link-hover-color:   @component-active-color;
    @list-group-link-heading-color: @text-color;
    
    
    //== Panels
    //
    //##
    
    @panel-bg:                    transparent;
    @panel-body-padding:          15px;
    @panel-heading-padding:       10px 15px;
    @panel-footer-padding:        @panel-heading-padding;
    @panel-border-radius:         @border-radius-base;
    
    //** Border color for elements within panels
    @panel-inner-border:          @component-active-bg;
    @panel-footer-bg:             @component-active-bg;
    
    @panel-default-text:          #fff;
    @panel-default-border:        @component-active-bg;
    @panel-default-heading-bg:    @component-active-bg;
    
    @panel-primary-text:          #fff;
    @panel-primary-border:        @brand-primary;
    @panel-primary-heading-bg:    @brand-primary;
    
    @panel-success-text:          @state-success-text;
    @panel-success-border:        @state-success-border;
    @panel-success-heading-bg:    @state-success-bg;
    
    @panel-info-text:             @state-info-text;
    @panel-info-border:           @state-info-border;
    @panel-info-heading-bg:       @state-info-bg;
    
    @panel-warning-text:          @state-warning-text;
    @panel-warning-border:        @state-warning-border;
    @panel-warning-heading-bg:    @state-warning-bg;
    
    @panel-danger-text:           @state-danger-text;
    @panel-danger-border:         @state-danger-border;
    @panel-danger-heading-bg:     @state-danger-bg;
    
    
    //== Thumbnails
    //
    //##
    
    //** Padding around the thumbnail image
    @thumbnail-padding:           4px;
    //** Thumbnail background color
    @thumbnail-bg:                @body-bg;
    //** Thumbnail border color
    @thumbnail-border:            #ddd;
    //** Thumbnail border radius
    @thumbnail-border-radius:     @border-radius-base;
    
    //** Custom text color for thumbnail captions
    @thumbnail-caption-color:     @text-color;
    //** Padding around the thumbnail caption
    @thumbnail-caption-padding:   9px;
    
    
    //== Wells
    //
    //##
    
    @well-bg:                     lighten(@component-active-bg, 2%);
    @well-border:                 transparent;
    
    
    //== Badges
    //
    //##
    
    @badge-color:                 #fff;
    //** Linked badge text color on hover
    @badge-link-hover-color:      #fff;
    @badge-bg:                    @btn-default-bg;
    
    //** Badge text color in active nav link
    @badge-active-color:          #fff;
    //** Badge background color in active nav link
    @badge-active-bg:             @badge-bg;
    
    @badge-font-weight:           bold;
    @badge-line-height:           1;
    @badge-border-radius:         10px;
    
    
    //== Breadcrumbs
    //
    //##
    
    @breadcrumb-padding-vertical:   8px;
    @breadcrumb-padding-horizontal: 15px;
    //** Breadcrumb background color
    @breadcrumb-bg:                 @component-active-bg;
    //** Breadcrumb text color
    @breadcrumb-color:              @gray;
    //** Text color of current page in the breadcrumb
    @breadcrumb-active-color:       @gray;
    //** Textual separator for between breadcrumb elements
    @breadcrumb-separator:          "/";
    
    
    //== Carousel
    //
    //##
    
    @carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);
    
    @carousel-control-color:                      #fff;
    @carousel-control-width:                      15%;
    @carousel-control-opacity:                    .5;
    @carousel-control-font-size:                  20px;
    
    @carousel-indicator-active-bg:                #fff;
    @carousel-indicator-border-color:             #fff;
    
    @carousel-caption-color:                      #fff;
    
    
    //== Close
    //
    //##
    
    @close-font-weight:           bold;
    @close-color:                 @text-color;
    @close-text-shadow:           none;
    
    
    //== Code
    //
    //##
    
    @code-color:                  #c7254e;
    @code-bg:                     #f9f2f4;
    
    @kbd-color:                   #fff;
    @kbd-bg:                      #333;
    
    @pre-bg:                      #f5f5f5;
    @pre-color:                   @gray-dark;
    @pre-border-color:            #ccc;
    @pre-scrollable-max-height:   340px;
    
    
    //== Type
    //
    //##
    
    //** Horizontal offset for forms and lists.
    @component-offset-horizontal: 180px;
    //** Text muted color
    @text-muted:                  @gray-light;
    //** Abbreviations and acronyms border color
    @abbr-border-color:           @gray-dark;
    //** Headings small color
    @headings-small-color:        @text-color;
    //** Blockquote small color
    @blockquote-small-color:      @text-color;
    //** Blockquote font size
    @blockquote-font-size:        (@font-size-base * 1.25);
    //** Blockquote border color
    @blockquote-border-color:     @gray-dark;
    //** Page header border color
    @page-header-border-color:    @component-active-bg;
    //** Width of horizontal description list titles
    @dl-horizontal-offset:        @component-offset-horizontal;
    //** Point at which .dl-horizontal becomes horizontal
    @dl-horizontal-breakpoint:    @grid-float-breakpoint;
    //** Horizontal line color.
    @hr-border:                   @gray-lighter;src/browser/less/theme/spacelab.less000066400000000000000000000004361516067322700200560ustar00rootroot00000000000000
    @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700';
    @import "../../../../node_modules/bootstrap/less/bootstrap";
    @import "../../../../node_modules/bootswatch/spacelab/variables";
    @import "../../../../node_modules/bootswatch/spacelab/bootswatch";
    @import "../default";
    src/browser/less/theme/superhero.less000066400000000000000000000004401516067322700203130ustar00rootroot00000000000000
    @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700';
    @import "../../../../node_modules/bootstrap/less/bootstrap";
    @import "../../../../node_modules/bootswatch/superhero/variables";
    @import "../../../../node_modules/bootswatch/superhero/bootswatch";
    @import "../default";
    src/browser/less/theme/united.less000066400000000000000000000004321516067322700175700ustar00rootroot00000000000000
    @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700';
    @import "../../../../node_modules/bootstrap/less/bootstrap";
    @import "../../../../node_modules/bootswatch/united/variables";
    @import "../../../../node_modules/bootswatch/united/bootswatch";
    @import "../default";
    src/browser/less/theme/yeti.less000066400000000000000000000004261516067322700172550ustar00rootroot00000000000000
    @path: 'https://fonts.googleapis.com/css?family=Roboto:300,400,700';
    @import "../../../../node_modules/bootstrap/less/bootstrap";
    @import "../../../../node_modules/bootswatch/yeti/variables";
    @import "../../../../node_modules/bootswatch/yeti/bootswatch";
    @import "../default";
    src/browser/less/tree.less000066400000000000000000000001131516067322700161310ustar00rootroot00000000000000#p3x-gitlist-tree-table {
      a {
        text-decoration: none !important;
      }
    }src/browser/less/treegraph.less000066400000000000000000000013731516067322700171640ustar00rootroot00000000000000.p3x-gitlist-treegraph-log {
    
    }
    
    .treegraph-button {
      padding: @padding-base-vertical !important;
      font-family: monospace;
    }
    
    
    .p3x-gitlist-light {
      .treegraph-button {
        background-color: @navbar-default-bg !important;
        color: @navbar-default-link-hover-color !important;
      }
      .treegraph-link {
        color: @brand-primary !important;
      }
    }
    
    .p3x-gitlist-dark {
      .treegraph-button {
        background-color: @navbar-inverse-bg !important;
        color: @navbar-inverse-link-hover-color !important;
      }
      .treegraph-link {
        color: @brand-primary !important;
      }
    }
    
    
    
    .p3x-gitlist-treegraph-subject {
      white-space: nowrap;
      p {
        display: inline;
        text-overflow: ellipsis;
      }
      a, a:active {
        color: @brand-primary !important;
      }
    }
    
    #rev-list  {
    }src/twig/000077500000000000000000000000001516067322700126305ustar00rootroot00000000000000src/twig/blame.twig000066400000000000000000000051461516067322700146120ustar00rootroot00000000000000{% extends 'layout-page.twig' %}
    
    {% set page = 'commits' %}
    
    {% block title %}P3X GitList{% endblock %}
    
    {% block content %}
    
        {% embed 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Blame', path:''}, {dir: file, path:''}]} %}
    
            {% block left %}
                {% if blames %}
                    Blames found: {{ blames | length }}
                    Mode: {{ type }}
                    Binary: {{ binary ? 'true' : 'false' }}
                {% endif %}
            {%  endblock %}
    
        {% endembed %}
    
    
    
        {% if blames %}
    
            

    Hang on, we reloading big blames...
    {% for blame in blames %}
       {{ blame.commitShort }} {% if not binary %} {% endif %}
    {% if not binary %}
    {{ blame.line }}
    {% endif %}
    {% endfor %} {% else %}

    No blames.

    {% endif %} {% endblock %} src/twig/breadcrumb.twig000066400000000000000000000105061516067322700156340ustar00rootroot00000000000000
     RSS  ZIP  TAR {% if app.show_http_remote or app.show_ssh_remote %}  Clone {% endif %} {% block right %}{% endblock %}
    {% block left %}{% endblock %} {% if app.show_http_remote or app.show_ssh_remote %} {% endif %}
    src/twig/browser.twig000066400000000000000000000041701516067322700152110ustar00rootroot00000000000000 src/twig/commit.twig000066400000000000000000000114311516067322700150140ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'commits' %} {% block title %}P3X GitList{% endblock %} {% block content %}

    Loading a big commit ...
    {% embed 'breadcrumb.twig' with {breadcrumbs: [{dir: "Commit #{commit.hash}", path:''}]} %} {% block left %} File changed: {{ commit.changedFiles }} {% endblock %} {% block right %} Browse code {% endblock %} {% endembed %}

    {{ commit.message }}{% if commit.body is not empty %}}
    {{ commit.body }}{% endif %}

    {{ commit.author.name }} authored on {{ commit.date | format_date }} {% if commit.author.email != commit.commiter.email %} • {{ commit.commiter.name }} committed on {{ commit.commiterDate | format_date }} {% endif %}
      {% for diff in commit.diffs %}
    •    {{ diff.file }}
      Diff lines: {{ diff.lineCount }} Type: {{ diff.binary ? 'binary' : 'text' }}
    • {% endfor %}
    {% endblock %} src/twig/commits-list.twig000066400000000000000000000063241516067322700161550ustar00rootroot00000000000000{% if commits %} {% for date, commit in commits %}
    {{ date | date("F j, Y") }}
      {% for item in commit %}
    • {{ item.message }}
      {{ item.author.name }} authored on {{ item.date | format_date }} {% if item.author.email != item.commiter.email %} • {{ item.commiter.name }} committed on {{ item.commiterDate | format_date }} {% endif %}
    • {% endfor %}
    {% endfor %} {% else %}

    No more commits.

    {% endif %} {% if page != 'searchcommits' %}
    {% if pager.current < pager.last - 1 %} Older / Next {% endif %}
    {% endif %} src/twig/commits.twig000066400000000000000000000005521516067322700152010ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'commits' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% embed 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Commit history', path:''}]} %} {# {% block left %} {% endblock %} #} {% endembed %} {% include 'commits-list.twig' %} {% endblock %} src/twig/error.twig000066400000000000000000000017321516067322700146600ustar00rootroot00000000000000{% extends 'layout.twig' %} {% block title %}P3X GitList{% endblock %} {% block body %} {% include 'navigation.twig' %}
    Oops! {{ message }}
    {% if error %}
    It is possible, that for huge commits, you need to increase the PHP memory limit, or sometimes, reloading is working, when you are not using lot's of many PHP processes. I tried to make huge commits to work with 128MB, but given, my server is using many PHP processes, sometimes the 128MB is not enough.
    {{ error | raw }}

    {% endif %} You may go back to previous page. {% include 'footer.twig' %}
    {% endblock %} src/twig/file.twig000066400000000000000000000150271516067322700144500ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'files' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% embed 'breadcrumb.twig' with {breadcrumbs: breadcrumbs} %} {% block right %}
    {% if extension == 'svg' %} {% endif %} {% embed 'modal/modal-commit.twig' with {type: 'delete', title: 'Delete', bodyTitle: 'Are you sure to delete this file?', wrapForm: true } %} {% block buttons %} {% endblock %} {% endembed %} {% if (((fileType != 'image' and fileType != 'markdown' ) or (enforceCodemirror)) and not binary )%} {% embed 'modal/modal-commit.twig' with { type: 'commit', title: 'Commit', bodyTitle: 'Are you sure to commit this change?', wrapForm: true } %} {% block buttons %} {% endblock %} {% endembed %} {% elseif fileType == 'markdown' %} Edit {% endif %}
    {% endblock %} {% block left %}
    {% if binary %} Download {% else %} Raw {% endif %} Blame History
    {% if (((fileType != 'image' and fileType != 'markdown') or enforceCodemirror) and not binary) %} {% endif %} {% if fileType == 'markdown' %} {% if enforceCodemirror %} Markdown rendered {% else %} Markdown code {% endif %} {% endif %}
    {% endblock %} {% endembed %}
    {% if fileType == 'image' %}
    {{ file }}
    {% elseif fileType == 'markdown' and enforceCodemirror != true %} {% include 'markdown.twig' with {'filename': file, 'data': blob} %} {% elseif not binary %}
    {{ blob|htmlentities|raw }}
    {% endif %} {% if extension == 'svg' %} {% endif %}
    {% endblock %} src/twig/footer.twig000066400000000000000000000002441516067322700150220ustar00rootroot00000000000000 src/twig/index.twig000066400000000000000000000053311516067322700146350ustar00rootroot00000000000000{% extends 'layout.twig' %} {% block title %}P3X GitList{% endblock %} {% block body %} {% include 'navigation.twig' %}
    {% for repository in repositories %}
    {{ repository.name | remove_extension }}
    {{ repository.time }} by {{ repository.user }} on {{ repository.branch }}
    {% if repository.description %} {{ repository.description }} {% else %} There is no repository description file. Please, create one to remove this message. {% endif %}
    {% endfor %}
    {% include 'footer.twig' %}
    {% endblock %} src/twig/layout-page.twig000066400000000000000000000037611516067322700157620ustar00rootroot00000000000000{% extends 'layout.twig' %} {% block body %} {% include 'navigation.twig' %}
    {% if page in ['commits', 'searchcommits'] %}
    {% else %}
    {% endif %} {% include 'menu.twig' %} {% block content %}{% endblock %}
    {% include 'footer.twig' %} {% endblock %} src/twig/markdown.twig000066400000000000000000000003111516067322700153410ustar00rootroot00000000000000
      {{ filename }}
    {{ data }}
    src/twig/menu.twig000066400000000000000000000020601516067322700144660ustar00rootroot00000000000000 src/twig/modal/000077500000000000000000000000001516067322700137245ustar00rootroot00000000000000src/twig/modal/modal-commit.twig000066400000000000000000000072451516067322700172120ustar00rootroot00000000000000{% if wrapForm %}
    {% endif %}
    {% if wrapForm %} {% endif %} src/twig/navigation.twig000066400000000000000000000112371516067322700156670ustar00rootroot00000000000000
    src/twig/network.twig000066400000000000000000000007621516067322700152220ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'network' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% include 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Network Graph ', path: ''}, {dir: commitishPath, path: ''}]} %}
    {#
    #}
    {% endblock %} src/twig/rss.twig000066400000000000000000000013621516067322700143350ustar00rootroot00000000000000 Latest commits in {{ repo }}:{{ branch }} RSS provided by GitList {{ url('homepage') }} {% for commit in commits %} {{ commit.message }} {{ commit.author.name }} authored {{ commit.shortHash }} in {{ commit.date | format_date }} {{ url('commit', {repo: repo, commit: commit.hash}) }} {{ commit.date | date('r') }} {{ commit.author.email }} ({{ commit.author.name }}) {% endfor %} src/twig/search.twig000066400000000000000000000056471516067322700150050ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'files' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% embed 'breadcrumb.twig' with {breadcrumbs: breadcrumbs} %} {% block left %} {% if results %} File: {{ results | length }} {% endif %} {% endblock %} {% endembed %} {% if results %}

    Hang on, we are loading a big search...
    {% for result in results %}
      {{ result.file }} on line {{ result.line }}   
    {{ result.match }}
    {% endfor %} {% else %}

    No result.

    {% endif %} {% endblock %} src/twig/searchcommits.twig000066400000000000000000000007741516067322700163750ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'searchcommits' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% embed 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Commits search results for: ' ~ query, path:''}]} %} {% block left %} {% if commits %} Commit: {{ commits | length }} {% endif %} {% endblock %} {% endembed %} {% include 'commits-list.twig' %} {% endblock %} src/twig/stats.twig000066400000000000000000000055641516067322700146740ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'stats' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% include 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Statistics', path:''}, {dir: branch, path:''}]} %}
      File extensions {{ stats.extensions|length }}
      {% for ext, amount in stats.extensions %}
    • {{ ext }} {{ amount }}
    • {% endfor %}
    {{ authors|length }}   Authors with commits
      Stats
    • {{ stats.files }} Total files
    • {{ stats.size }} bytes {{ stats.size | format_size }}
    {% endblock %} src/twig/tree.twig000066400000000000000000000160351516067322700144700ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'files' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% embed 'breadcrumb.twig' with {breadcrumbs: breadcrumbs } %} {% block left %}
    {% embed 'modal/modal-commit.twig' with {type: 'new', title: 'New file', bodyTitle: 'Are you sure to add a new file or directory?
    If you add a slash to the end of the "filename", you create a directory, otherwise you create a file.
    Please, make sure you use a relative path.


    Note: The filename is not created on the current tree browser you are, but that actual root of the repo.', wrapForm: true } %} {% block form %}

    {% endblock %} {% block buttons %} {% endblock %} {% endembed %} {% embed 'modal/modal-commit.twig' with {enctype: 'multipart/form-data', type: 'new-binary', title: 'New binary', bodyTitle: 'Are you sure to add a new binary file?', wrapForm: true } %} {% block form %}

    {% endblock %} {% block buttons %} {% endblock %} {% endembed %}
    {% endblock %} {% endembed %} {% if files is not empty %} {% if parent is not null %} {% endif %} {% for file in files %} {% endfor %}
    Name Mode Size
    {% if not parent %} .. {% else %} .. {% endif %}
    {%- if file.type == "folder" or file.type == "symlink" -%}   {{ file.name }} {%- elseif file.type == "module" -%}   {{ file.name }} @ {{ file.shortHash }} {%- else -%}   {{ file.name }} {%- endif -%} {{ file.mode }} {% if file.size %}{{ (file.size / 1024) | number_format }} kb{% endif %}
    {% else %}

    This repository is empty.

    {% endif %} {% if readme is defined and readme is not empty %} {% include 'markdown.twig' with {'filename': readme.filename, 'data': readme.content} %} {% endif %} {% endblock %} src/twig/treegraph.twig000066400000000000000000000040471516067322700155120ustar00rootroot00000000000000{% extends 'layout-page.twig' %} {% set page = 'treegraph' %} {% block title %}P3X GitList{% endblock %} {% block content %} {% embed 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Graph', path:''}]} %} {% block right %} {% endblock %} {% block left %} Hover over the log to see the full commit message. {% endblock %} {% endembed %}
      {% for item in graphItems %}
    • {{ item.relation }}
    • {% endfor %}
      {% for item in graphItems %}
    • {% if item.rev is defined %} {{ item.short_rev }} {{ item.branch }} {{ item.date | date(gitlist_date_format) }} by {{ item.author }}   {{ item.subject }} {% else %} {% endif %}
    • {% endfor %}
    {% endblock %} tests/000077500000000000000000000000001516067322700122315ustar00rootroot00000000000000tests/Gitlist/000077500000000000000000000000001516067322700136505ustar00rootroot00000000000000tests/Gitlist/RepositoryTest.php000066400000000000000000000017061516067322700174040ustar00rootroot00000000000000prophesize(Client::class); $client->run(Argument::type(Repository::class), "grep -i --line-number -- '=sleep 5;' master")->shouldBeCalled(); $repository = new Repository('/tmp', $client->reveal()); $repository->searchTree('--open-files-in-pager=sleep 5;', 'master'); $repository->searchTree('-O=sleep 5;', 'master'); } public function testIsSanitizingSearchWithAnyOption() { $client = $this->prophesize(Client::class); $client->run(Argument::type(Repository::class), "grep -i --line-number -- 'foobar =bar;' foo")->shouldBeCalled(); $repository = new Repository('/tmp', $client->reveal()); $repository->searchTree('foobar --bar --foo=bar;', 'foo'); } }tests/Gitter/000077500000000000000000000000001516067322700134675ustar00rootroot00000000000000tests/Gitter/ClientTest.php000077500000000000000000000054141516067322700162650ustar00rootroot00000000000000mkdir(self::$tmpdir); if (!is_writable(self::$tmpdir)) { $this->markTestSkipped('There are no write permissions in order to create test repositories.'); } } public function setUp() { if (!is_writable(self::$tmpdir)) { $this->markTestSkipped('There are no write permissions in order to create test repositories.'); } $path = getenv('GIT_CLIENT') ?: null; $this->client = new Client($path); } /** * @expectedException RuntimeException */ public function testIsNotAbleToGetUnexistingRepository() { $this->client->getRepository(self::$tmpdir . '/testrepo'); } public function testIsParsingGitVersion() { $version = $this->client->getVersion(); $this->assertNotEmpty($version); } public function testIsCreatingRepository() { $repository = $this->client->createRepository(self::$tmpdir . '/testrepo'); $fs = new Filesystem(); $fs->remove(self::$tmpdir . '/testrepo/.git/description'); $this->assertRegExp("/nothing to commit/", $repository->getClient()->run($repository, 'status')); } public function testIsCreatingBareRepository() { $repository = $this->client->createRepository(self::$tmpdir . '/testbare', true); $this->assertInstanceOf('Gitter\Repository', $repository); } /** * @expectedException RuntimeException */ public function testIsNotAbleToCreateRepositoryDueToExistingOne() { $this->client->createRepository(self::$tmpdir . '/testrepo'); } /** * @expectedException RuntimeException */ public function testIsNotOpeningHiddenRepositories() { $this->client->getRepository(self::$tmpdir . '/hiddenrepo'); } /** * @expectedException RuntimeException */ public function testIsCatchingGitCommandErrors() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $repository->getClient()->run($repository, 'wrong'); } public static function tearDownAfterClass() { $fs = new Filesystem(); $fs->remove(self::$tmpdir); } } tests/Gitter/Model/000077500000000000000000000000001516067322700145275ustar00rootroot00000000000000tests/Gitter/Model/Commit/000077500000000000000000000000001516067322700157575ustar00rootroot00000000000000tests/Gitter/Model/Commit/CommitTest.php000066400000000000000000000037251516067322700205670ustar00rootroot00000000000000 '209908f247194b1adc836f2e50f957cb1f11f41c', 'short_hash' => '209908f', 'tree' => '0a1f6638ccfc6d6b34be8a913144304355d23cc3', 'parents' => '6e6951114ccf7b162e2a57b0462b39ca972f476f 1e8fd833f71fd20f8b176c79c705b9f096434126', 'author' => 'The Author', 'author_email' => 'author@example.com', 'date' => '1347372763', 'commiter' => 'The Commiter', 'commiter_email' => 'commiter@example.com', 'commiter_date' => '1347372763', 'message' => 'Test commit', 'body' => 'Test body' ); $commit = new Commit(); $commit->importData($data); $this->assertEquals('209908f247194b1adc836f2e50f957cb1f11f41c', $commit->getHash()); $this->assertEquals('209908f', $commit->getShortHash()); $this->assertEquals('0a1f6638ccfc6d6b34be8a913144304355d23cc3', $commit->getTreeHash()); $this->assertEquals(array('6e6951114ccf7b162e2a57b0462b39ca972f476f', '1e8fd833f71fd20f8b176c79c705b9f096434126'), $commit->getParentsHash()); $this->assertEquals('The Author', $commit->getAuthor()->getName()); $this->assertEquals('author@example.com', $commit->getAuthor()->getEmail()); $this->assertEquals(new DateTime('@1347372763'), $commit->getDate()); $this->assertEquals('The Commiter', $commit->getCommiter()->getName()); $this->assertEquals('commiter@example.com', $commit->getCommiter()->getEmail()); $this->assertEquals(new DateTime('@1347372763'), $commit->getCommiterDate()); $this->assertEquals('Test commit', $commit->getMessage()); $this->assertEquals('Test body', $commit->getBody()); } } tests/Gitter/PrettyFormatTest.php000066400000000000000000000025131516067322700175010ustar00rootroot00000000000000assertEquals($expected, $format->parse($xml)); } public function dataForTestIsParsingPrettyXMLFormat() { return array( array( 'valuevalue2', array(array('tag' => 'value', 'tag2' => 'value2')), ), array( '', array(array('empty_tag' => '')), ), array( 'item 1item 2', array(array('tag' => 'item 1'), array('tag' => 'item 2')), ), array( 'value', array(array('tag' => array(array('inner_tag' => 'value')))), ) ); } /** * @expectedException RuntimeException */ public function testIsNotParsingWithoutData() { $format = new PrettyFormat; $format->parse(''); } } tests/Gitter/RepositoryTest.php000077500000000000000000000532421516067322700172300ustar00rootroot00000000000000mkdir(self::$tmpdir); if (!is_writable(self::$tmpdir)) { $this->markTestSkipped('There are no write permissions in order to create test repositories.'); } } public function setUp() { if (!is_writable(self::$tmpdir)) { $this->markTestSkipped('There are no write permissions in order to create test repositories.'); } $path = getenv('GIT_CLIENT') ?: null; $this->client = new Client($path); } public function tearDown () { \Mockery::close(); } public function testIsCreatingRepositoryFixtures() { $a = $this->client->createRepository(self::$tmpdir . '/testrepo'); $b = $this->client->createRepository(self::$tmpdir . '/anothertestrepo'); $c = $this->client->createRepository(self::$tmpdir . '/bigbadrepo'); $this->assertRegExp("/nothing to commit/", $a->getClient()->run($a, 'status')); $this->assertRegExp("/nothing to commit/", $b->getClient()->run($b, 'status')); $this->assertRegExp("/nothing to commit/", $c->getClient()->run($c, 'status')); } public function testIsConfiguratingRepository() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.email', 'luke@rebel.org'); $this->assertEquals($repository->getConfig('user.name'), 'Luke Skywalker'); $this->assertEquals($repository->getConfig('user.email'), 'luke@rebel.org'); } public function testIsNamesCorrect() { $a = $this->client->createRepository(self::$tmpdir . '/reponame'); $b = $this->client->createRepository(self::$tmpdir . '/another-repo-name/'); $this->assertEquals("reponame", $a->getName()); $this->assertEquals("another-repo-name", $b->getName()); } public function testIsAdding() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); file_put_contents(self::$tmpdir . '/testrepo/test_file.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); $repository->add('test_file.txt'); $this->assertRegExp("/new file: test_file.txt/", $repository->getClient()->run($repository, 'status')); } /** * @depends testIsAdding */ public function testIsAddingDot() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); file_put_contents(self::$tmpdir . '/testrepo/test_file1.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); file_put_contents(self::$tmpdir . '/testrepo/test_file2.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); file_put_contents(self::$tmpdir . '/testrepo/test_file3.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); $repository->add(); $this->assertRegExp("/new file: test_file1.txt/", $repository->getClient()->run($repository, 'status')); $this->assertRegExp("/new file: test_file2.txt/", $repository->getClient()->run($repository, 'status')); $this->assertRegExp("/new file: test_file3.txt/", $repository->getClient()->run($repository, 'status')); } /** * @depends testIsAddingDot */ public function testIsAddingAll() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); file_put_contents(self::$tmpdir . '/testrepo/test_file4.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); file_put_contents(self::$tmpdir . '/testrepo/test_file5.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); file_put_contents(self::$tmpdir . '/testrepo/test_file6.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); $repository->addAll(); $this->assertRegExp("/new file: test_file4.txt/", $repository->getClient()->run($repository, 'status')); $this->assertRegExp("/new file: test_file5.txt/", $repository->getClient()->run($repository, 'status')); $this->assertRegExp("/new file: test_file6.txt/", $repository->getClient()->run($repository, 'status')); } /** * @depends testIsAddingAll */ public function testIsAddingArrayOfFiles() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); file_put_contents(self::$tmpdir . '/testrepo/test_file7.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); file_put_contents(self::$tmpdir . '/testrepo/test_file8.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); file_put_contents(self::$tmpdir . '/testrepo/test_file9.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); $repository->add(array('test_file7.txt', 'test_file8.txt', 'test_file9.txt')); $this->assertRegExp("/new file: test_file7.txt/", $repository->getClient()->run($repository, 'status')); $this->assertRegExp("/new file: test_file8.txt/", $repository->getClient()->run($repository, 'status')); $this->assertRegExp("/new file: test_file9.txt/", $repository->getClient()->run($repository, 'status')); } /** * @depends testIsAddingArrayOfFiles */ public function testIsCommiting() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $repository->commit("The truth unveiled\n\nThis is a proper commit body"); $this->assertRegExp("/The truth unveiled/", $repository->getClient()->run($repository, 'log')); $this->assertRegExp("/This is a proper commit body/", $repository->getClient()->run($repository, 'log')); } public function testIsCreatingBranches() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $repository->createBranch('issue12'); $repository->createBranch('issue42'); $branches = $repository->getBranches(); $this->assertContains('issue12', $branches); $this->assertContains('issue42', $branches); $this->assertContains('master', $branches); } public function testIsCreatingTags() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $repository->createTag('1.0.0'); $repository->createTag('1.0.1', 'annotated tag'); $tags = $repository->getTags(); $this->assertContains('1.0.0', $tags); $this->assertContains('1.0.1', $tags); } public function testIsGettingCurrentBranch() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $branch = $repository->getCurrentBranch(); $this->assertTrue($branch === 'master'); $commits = $repository->getCommits(); $hash = $commits[0]->getHash(); $repository->checkout($hash); $new_branch = $repository->getCurrentBranch(); # $this->assertTrue($new_branch === NULL); $repository->checkout($branch); } public function testIsGettingBranchesWhenHeadIsDetached() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $commits = $repository->getCommits(); $current_branch = $repository->getCurrentBranch(); $hash = $commits[0]->getHash(); $repository->checkout($hash); $branches = $repository->getBranches(); $this->assertTrue(count($branches) === 3); $branch = $repository->getHead('develop'); $repository->checkout($current_branch); } public function testIsCheckingIfBranchExists() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $this->assertTrue($repository->hasBranch('issue12')); } public function testIsCheckingOut() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $branch = $repository->checkout('issue12'); $branch = $repository->getCurrentBranch(); $this->assertTrue($branch === 'issue12'); $repository->checkout('master'); $branch = $repository->getCurrentBranch(); $this->assertTrue($branch === 'master'); } /** * @depends testIsCommiting */ public function testIsGettingCommits() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $commits = $repository->getCommits(); foreach ($commits as $commit) { $this->assertTrue($commit->isCommit()); $this->assertInstanceOf('Gitter\Model\Commit\Commit', $commit); $this->assertEquals($commit->getMessage(), 'The truth unveiled'); $this->assertInstanceOf('Gitter\Model\Commit\Author', $commit->getAuthor()); $this->assertEquals($commit->getAuthor()->getName(), 'Luke Skywalker'); $this->assertEquals($commit->getAuthor()->getEmail(), 'luke@rebel.org'); $this->assertEquals($commit->getCommiter()->getName(), 'Luke Skywalker'); $this->assertEquals($commit->getCommiter()->getEmail(), 'luke@rebel.org'); $this->assertEquals($commit->getParentsHash(), array()); $this->assertInstanceOf('DateTime', $commit->getDate()); $this->assertInstanceOf('DateTime', $commit->getCommiterDate()); $this->assertRegExp('/[a-f0-9]+/', $commit->getHash()); $this->assertRegExp('/[a-f0-9]+/', $commit->getShortHash()); $this->assertRegExp('/[a-f0-9]+/', $commit->getTreeHash()); } } /** * @depends testIsGettingCommits */ public function testIsGettingCommitsFromSpecificFile() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $commits = $repository->getCommits('test_file4.txt'); foreach ($commits as $commit) { $this->assertTrue($commit->isCommit()); $this->assertInstanceOf('Gitter\Model\Commit\Commit', $commit); $this->assertEquals($commit->getMessage(), "The truth unveiled"); $this->assertInstanceOf('Gitter\Model\Commit\Author', $commit->getAuthor()); $this->assertEquals($commit->getAuthor()->getName(), 'Luke Skywalker'); $this->assertEquals($commit->getAuthor()->getEmail(), 'luke@rebel.org'); } } public function testIsGettingTree() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $files = $repository->getTree('master'); foreach ($files as $file) { $this->assertTrue($file->isBlob()); $this->assertInstanceOf('Gitter\Model\Blob', $file); $this->assertRegExp('/test_file[0-9]*.txt/', $file->getName()); $this->assertEquals($file->getSize(), '55'); $this->assertEquals($file->getMode(), '100644'); $this->assertRegExp('/[a-f0-9]+/', $file->getHash()); } } public function testIsGettingTreeOutput() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $files = $repository->getTree('master')->output(); foreach ($files as $file) { $this->assertEquals('blob', $file['type']); $this->assertRegExp('/test_file[0-9]*.txt/', $file['name']); $this->assertEquals($file['size'], '55'); $this->assertEquals($file['mode'], '100644'); $this->assertRegExp('/[a-f0-9]+/', $file['hash']); } } public function testIsGettingTreesWithinTree() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); // Creating folders mkdir(self::$tmpdir . '/testrepo/MyFolder'); mkdir(self::$tmpdir . '/testrepo/MyTest'); mkdir(self::$tmpdir . '/testrepo/MyFolder/Tests'); // Populating created folders file_put_contents(self::$tmpdir . '/testrepo/MyFolder/crazy.php', 'Lorem ipsum dolor sit amet'); file_put_contents(self::$tmpdir . '/testrepo/MyFolder/skywalker.php', 'Lorem ipsum dolor sit amet'); file_put_contents(self::$tmpdir . '/testrepo/MyTest/fortytwo.php', 'Lorem ipsum dolor sit amet'); file_put_contents(self::$tmpdir . '/testrepo/MyFolder/Tests/web.php', 'Lorem ipsum dolor sit amet'); file_put_contents(self::$tmpdir . '/testrepo/MyFolder/Tests/cli.php', 'Lorem ipsum dolor sit amet'); // Adding and commiting $repository->addAll(); $repository->commit("Creating folders for testIsGettingTreesWithinTrees"); // Checking tree $files = $repository->getTree('master')->output(); $this->assertEquals('folder', $files[0]['type']); $this->assertEquals('MyFolder', $files[0]['name']); $this->assertEquals('', $files[0]['size']); $this->assertEquals('040000', $files[0]['mode']); $this->assertEquals('4143e982237f3bdf56b5350f862c334054aad69e', $files[0]['hash']); $this->assertEquals('folder', $files[1]['type']); $this->assertEquals('MyTest', $files[1]['name']); $this->assertEquals('', $files[1]['size']); $this->assertEquals('040000', $files[1]['mode']); $this->assertEquals('632240595eabd59e4217d196d6c12efb81f9c011', $files[1]['hash']); $this->assertEquals('blob', $files[2]['type']); $this->assertEquals('test_file.txt', $files[2]['name']); $this->assertEquals('55', $files[2]['size']); $this->assertEquals('100644', $files[2]['mode']); $this->assertEquals('a773bfc0fda6f878e3d17d78c667d18297c8831f', $files[2]['hash']); } public function testIsGettingBlobsWithinTrees() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $files = $repository->getTree('master:MyFolder/')->output(); $this->assertEquals('folder', $files[0]['type']); $this->assertEquals('Tests', $files[0]['name']); $this->assertEquals('', $files[0]['size']); $this->assertEquals('040000', $files[0]['mode']); $this->assertEquals('8542f67d011ff2ea5ec49a729ba81a52935676d1', $files[0]['hash']); $this->assertEquals('blob', $files[1]['type']); $this->assertEquals('crazy.php', $files[1]['name']); $this->assertEquals('26', $files[1]['size']); $this->assertEquals('100644', $files[1]['mode']); $this->assertEquals('d781006b2d05cc31751954a0fb920c990e825aad', $files[1]['hash']); $this->assertEquals('blob', $files[2]['type']); $this->assertEquals('skywalker.php', $files[2]['name']); $this->assertEquals('26', $files[2]['size']); $this->assertEquals('100644', $files[2]['mode']); $this->assertEquals('d781006b2d05cc31751954a0fb920c990e825aad', $files[2]['hash']); } public function testIsGettingBlobOutput() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $blob = $repository->getBlob('master:MyFolder/crazy.php')->output(); $this->assertEquals('Lorem ipsum dolor sit amet', $blob); $blob = $repository->getBlob('master:test_file4.txt')->output(); $this->assertEquals('Your mother is so ugly, glCullFace always returns TRUE.', $blob); } public function testIsGettingSymlinksWithinTrees() { if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $this->markTestSkipped('Unable to run on Windows'); } $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $fs = new Filesystem(); $fs->touch(self::$tmpdir . '/testrepo/original_file.txt'); $fs->symlink(self::$tmpdir . '/testrepo/original_file.txt', self::$tmpdir . '/testrepo/link.txt'); $repository->addAll(); $repository->commit("Testing symlinks"); $files = $repository->getTree('master'); foreach ($files as $file) { if ($file instanceof Symlink) { $this->assertEquals($file->getPath(), self::$tmpdir . '/testrepo/original_file.txt'); $this->assertEquals($file->getName(), 'link.txt'); $this->assertEquals($file->getMode(), '120000'); return; } } $this->fail('No symlink found inside tree'); } public function testIsGettingSymlinksWithinTreesOutput() { if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $this->markTestSkipped('Unable to run on Windows'); } $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $fs = new Filesystem(); $fs->touch(self::$tmpdir . '/testrepo/original_file.txt'); $fs->symlink(self::$tmpdir . '/testrepo/original_file.txt', self::$tmpdir . '/testrepo/link2.txt'); $repository->addAll(); $repository->commit("Testing symlinks"); $files = $repository->getTree('master')->output(); foreach ($files as $file) { if ($file['type'] == 'symlink') { $this->assertEquals($file['path'], self::$tmpdir . '/testrepo/original_file.txt'); $this->assertEquals($file['name'], 'link.txt'); $this->assertEquals($file['mode'], '120000'); return; } } $this->fail('No symlink found inside tree output'); } public function testIsGettingTotalCommits() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $this->assertEquals($repository->getTotalCommits(), '4'); } public function testIsGettingCommit() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $commits = $repository->getCommits(); foreach ($commits as $commit) { $singleCommit = $repository->getCommit($commit->getHash()); $this->assertTrue($singleCommit->isCommit()); $this->assertInstanceOf('Gitter\Model\Commit\Commit', $singleCommit); $this->assertInstanceOf('Gitter\Model\Commit\Author', $singleCommit->getAuthor()); $this->assertEquals($singleCommit->getAuthor()->getName(), 'Luke Skywalker'); $this->assertEquals($singleCommit->getAuthor()->getEmail(), 'luke@rebel.org'); $this->assertEquals($singleCommit->getCommiter()->getName(), 'Luke Skywalker'); $this->assertEquals($singleCommit->getCommiter()->getEmail(), 'luke@rebel.org'); $this->assertInstanceOf('DateTime', $singleCommit->getDate()); $this->assertInstanceOf('DateTime', $singleCommit->getCommiterDate()); $this->assertRegExp('/[a-f0-9]+/', $singleCommit->getHash()); $this->assertRegExp('/[a-f0-9]+/', $singleCommit->getShortHash()); $this->assertRegExp('/[a-f0-9]+/', $singleCommit->getTreeHash()); if ($singleCommit->getMessage() == 'The truth unveiled') { $this->assertEquals($singleCommit->getBody(), 'This is a proper commit body'); } } } public function testIsGettingCurrentHead() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $this->assertEquals($repository->getHead(), 'master'); } public function testIsGettingBranchTree() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $this->assertRegExp('/[a-f0-9]+/', $repository->getBranchTree('issue12')); } public function testIsGettingBlame() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); $blame = $repository->getBlame('test_file4.txt'); $this->assertEquals("Your mother is so ugly, glCullFace always returns TRUE.", trim($blame[1]['line'])); $this->assertEquals($repository->getBlame('original_file.txt'), array()); } public function testIsAddingFileNameWithSpace() { $repository = $this->client->getRepository(self::$tmpdir . '/testrepo'); file_put_contents(self::$tmpdir . '/testrepo/test file10.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); $repository->add('test file10.txt'); $this->assertRegExp("/new file: test file10.txt/", $repository->getClient()->run($repository, 'status')); } public function testCommitWithFileNameWithSpace() { $repo = $this->client->createRepository(self::$tmpdir . '/testrepospace'); $diffs = $repo->readDiffLogs($this->getLogsForCommitWithFileNameWithSpace()); $this->assertEquals('test file.txt', $diffs[0]->getFile(), 'New file name with a space in it'); $this->assertEquals('testfile.txt', $diffs[1]->getFile(), 'Old file name'); } public static function tearDownAfterClass() { $fs = new Filesystem(); $fs->remove(self::$tmpdir); } private function getLogsForCommitWithFileNameWithSpace() { // 'testfile.txt' is renamed to 'test file.txt' return array( 'diff --git a/test file.txt b/test file.txt', 'new file mode 100644', 'index 0000000..63edbe7', '--- /dev/null', '+++ b/test file.txt', '@@ -0,0 +1 @@', '+Modified line', 'diff --git a/testfile.txt b/testfile.txt', 'deleted file mode 100644', 'index 63edbe7..0000000', '--- a/testfile.txt', '+++ /dev/null', '@@ -1 +0,0 @@', '-Modified line', ); } } tests/Gitter/Util/000077500000000000000000000000001516067322700144045ustar00rootroot00000000000000tests/Gitter/Util/DateTimeTest.php000066400000000000000000000016021516067322700174500ustar00rootroot00000000000000assertEquals($date->format('Y-m-d'), '2010-01-28'); } public function testIsCreatingWithoutTimezone() { $date = new DateTime('2012-10-10 00:00:00'); $this->assertEquals($date->format('Y-m-d'), '2012-10-10'); } public function testIsCreatingWithUnixTimestamp() { $date = new DateTime('@632988000'); $this->assertEquals($date->format('Y-m-d'), '1990-01-22'); } public function testIsCreatingWithUnixTimestampAndTimezone() { $date = new DateTime('@632988000', new \DateTimeZone('UTC')); $this->assertEquals($date->format('Y-m-d'), '1990-01-22'); } }todo.md000066400000000000000000000041441516067322700123610ustar00rootroot00000000000000[//]: #@corifeus-header # 🤖 P3X Gitlist - An enhanced elegant, feature rich and modern git ui repository viewer [//]: #@corifeus-header:end ## TODO * Localization (twig, controller, php, js) * Sometimes, I try to upload a file like `grub.png` and it does not work, while I upload a different `.png` and it works, weird * make sure new functions work with Windows or disable some features is Windows * treegraph pager * diff by file * pager not working right with commits * search has no pager (either of two) * Basically, the twig "for" is not cheap => expensive, use AJAX and web worker * search.twig * blame.twig * Search, at work, "fix" string hs 1221 results, use AJAX and pager * Blames in gitlist on composer.lock has 994 results, use AJAX and pager * In submodules, if the "submodule" and "path" is not the same, it chokes (it should work the submodule name and path are not the same) * Works * submodule "path/name" * path path/name * Not working * submodule "name" * path path/name * In submodules, the url cannot have slash at the end * Convert Silex 2 to Symfony 4 - actually this is stays as Silex, Symfony is overcomplicated for a small web site [//]: #@corifeus-footer --- [**P3X-GITLIST**](https://pages.corifeus.com/gitlist) Build v2018.12.13-4 [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) ## P3X Sponsors [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![NoSQLBooster](https://cdn.corifeus.com/assets/png/nosqlbooster-70x70.png)](https://www.nosqlbooster.com/) [The Smartest IDE for MongoDB](https://www.nosqlbooster.com) [//]: #@corifeus-footer:endwebpack.config.js000066400000000000000000000142131516067322700143060ustar00rootroot00000000000000const fs = require('fs').promises const path = require('path'); const webpack = require('webpack'); const utils = require('corifeus-utils') const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin') const WebpackOnBuildPlugin = require('on-build-webpack'); const fileAsset = `[name].[hash].[ext]`; const minimize = process.argv.includes('--production'); const mode = minimize ? 'development' : 'production'; let minimizer = undefined; const prodDir = require('./package').corifeus["prod-dir"]; const buildDir = __dirname + `/public/${prodDir}/webpack`; let devtool; const plugins = [ new ExtractTextPlugin({ filename: '[name].[hash].css', disable: false, allChunks: true }), new HtmlWebpackPlugin({ template: `${__dirname}/src/browser/layout.tpl.twig`, inject: 'body', chunksSortMode: 'dependency', chunks: ['bundle'], filename: `${__dirname}/src/twig/layout.twig`, }), ]; plugins.push( new WebpackOnBuildPlugin(async (stats) => { try { const newFileNames = Object.keys(stats.compilation.assets).map(file => path.resolve(`${buildDir}/${file}`)); const baseDir = path.resolve(buildDir); const baseDirList = await utils.fs.readdirRecursive(baseDir) const promises = []; for(let baseDirFile of baseDirList) { if (!newFileNames.includes(baseDirFile)) { promises.push( fs.unlink(baseDirFile) ) } } await Promise.all(promises); } catch(e) { console.error(e) process.exit(-1) } }), ) if (minimize) { const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); plugins.push( new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, // cssProcessor: require('cssnano'), // cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, canPrint: true }) ) devtool = false; const bannerText = require('corifeus-builder').utils.license(); minimizer = [ new TerserPlugin({ sourceMap: true, parallel: true, cache: true, extractComments: { condition: /^\**!|@preserve|@license|@cc_on/, filename: function (fileName) { return `${fileName}.LICENSE.txt`; }, banner: function (webpackBanner) { return ` ${bannerText} For more information about all licenses, please see ${webpackBanner} `; } }, terserOptions: { compress: { warnings: false }, ecma: 8, // todo found out if mangle use or not // mangle: false === keep function names // mangle: true === drop function names mangle: false, sourceMap: true, comments: false, beautify: false }, }), ] plugins.push( new webpack.BannerPlugin({ banner: bannerText, include: /\.css$/, exclude: /\.ts$|\.js$/, // hash:[hash], chunkhash:[chunkhash], name:[name], filebase:[filebase], query:[query], file:[file] }) ) plugins.push( new webpack.SourceMapDevToolPlugin({ filename: 'sourcemaps/[file].map', append: '\n//# sourceMappingURL=./[url]' }) ) } const fileLoader = [ { loader: 'file-loader', options: { name: fileAsset, outputPath: 'assets', context: 'assets', // publicPath: 'webpack/assets', // useRelativePath: true, } } ] module.exports = { // watch: true, devtool: devtool, entry: { bundle: "./src/browser/bundle.js", }, output: { path: buildDir, filename: '[name].[hash].js', chunkFilename: '[id].[hash].chunk.js', // publicPath: '{{ app.url_subdir }}/webpack/', publicPath: `./${prodDir}/webpack/`, }, module: { rules: [ { test: /\.less$/, use: [{ loader: 'style-loader' , }, { loader: 'css-loader', }, { loader: 'less-loader', }], }, { test: /\.html$/, use: [{ loader: 'html-loader', options: { minimize: mode, caseSensitive: true } }] }, { test: /\.(png|jpe?g|gif|ico)$/, use: fileLoader }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, use: fileLoader }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, use: fileLoader }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: fileLoader }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: fileLoader }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: fileLoader }, { test: /\.css$/, use: ExtractTextPlugin.extract({ // fallback: "style-loader", use: [ { loader: 'css-loader', options: { // in v2 it throws error // minimize: minimize, sourceMap: true }, }] }) } ] }, optimization: { minimize: minimize, minimizer: minimizer }, plugins: plugins, mode: mode, }