├── .gitignore ├── README.md ├── __init__.py ├── daemon ├── __init__.py ├── dropbox_sync_repo.py ├── dropbox_sync_repo.sh ├── eventworker.py ├── eventworker.sh ├── forkworker.py ├── forkworker.sh ├── hookworker.py ├── hookworker.sh ├── importrepoworker.py ├── importrepoworker.sh ├── index.sql ├── models.py ├── notifworker.py ├── notifworker.sh ├── statsworker.py ├── test_redis.py ├── tests.py ├── triggerworker.py ├── userstatsjob.py └── views.py ├── dist ├── __init__.py ├── models.py ├── tests.py └── views.py ├── explore ├── __init__.py ├── models.py ├── tests.py └── views.py ├── feed ├── __init__.py ├── feed.py ├── mailUtils.py ├── models.py ├── tests.py └── views.py ├── githooks ├── __init__.py ├── diff-tree-blob-size.sh ├── git-import-remote-repo.sh ├── git-pretty-diff.sh ├── git-pretty-log.sh ├── git-pullrequest.sh ├── gitbare │ ├── HEAD │ ├── config │ ├── description │ ├── hooks │ │ ├── post-receive │ │ ├── post-update │ │ └── pre-receive │ └── info │ │ └── exclude ├── models.py ├── tests.py ├── update └── views.py ├── gssettings ├── Form.py ├── __init__.py ├── models.py ├── tests.py └── views.py ├── gsuser ├── Forms.py ├── __init__.py ├── decorators.py ├── middleware.py ├── models.py ├── tests.py ├── utils.py └── views.py ├── help ├── Forms.py ├── __init__.py ├── models.py ├── tests.py └── views.py ├── issue ├── Forms.py ├── __init__.py ├── cons.py ├── models.py ├── tests.py └── views.py ├── keyauth ├── __init__.py ├── models.py ├── tests.py └── views.py ├── keyvalue ├── __init__.py ├── models.py ├── tests.py └── views.py ├── manage.py ├── objectscache ├── __init__.py ├── da.py ├── models.py ├── tests.py └── views.py ├── render_settins.py ├── repo ├── Forms.py ├── __init__.py ├── githandler.py ├── models.py ├── tests.py └── views.py ├── settings.tmpl ├── skills ├── __init__.py ├── models.py ├── tests.py └── views.py ├── stats ├── __init__.py ├── models.py ├── tests.py ├── timeutils.py └── views.py ├── team ├── __init__.py ├── decorators.py ├── models.py ├── tests.py └── views.py ├── templates ├── base.html ├── email.html ├── explore │ └── explore.html ├── help │ ├── access_out_of_limit.html │ ├── default.html │ ├── error.html │ └── error_with_reason.html ├── index.html ├── repo │ ├── advance.html │ ├── blob.html │ ├── branch_nav.html │ ├── branch_permission.html │ ├── branches.html │ ├── branches_permission.html │ ├── commit.html │ ├── commits.html │ ├── compare.html │ ├── condition.html │ ├── create.html │ ├── delete.html │ ├── detail.html │ ├── hooks.html │ ├── issue_create.html │ ├── issue_edit.html │ ├── issue_show.html │ ├── issues.html │ ├── issues_show.html │ ├── members.html │ ├── network.html │ ├── permission.html │ ├── pull_new.html │ ├── pull_show.html │ ├── pulls.html │ ├── pulse.html │ ├── refs_create.html │ ├── refs_graph.html │ ├── repo.html │ ├── settings_base.html │ ├── shared │ │ └── base_fields.html │ ├── stats.html │ ├── tags.html │ ├── tree.html │ ├── url.html │ └── user_repo.html ├── settings │ ├── base.html │ ├── change_username_email.html │ ├── changepassword.html │ ├── default.html │ ├── destroy.html │ ├── emails.html │ ├── notif.html │ ├── profile.html │ ├── sshpubkey.html │ ├── team.html │ ├── team_create.html │ ├── thirdparty.html │ └── validate_email.html ├── skills │ └── skills.html ├── team │ ├── dashboard_base.html │ ├── destroy.html │ ├── group.html │ ├── groups.html │ ├── issues.html │ ├── members.html │ ├── notif.html │ ├── profile.html │ ├── pull_merge.html │ ├── pull_request.html │ ├── repo.html │ ├── settings_base.html │ └── timeline.html └── user │ ├── active.html │ ├── dashboard_base.html │ ├── feed.html │ ├── issues.html │ ├── join.html │ ├── login.html │ ├── notif.html │ ├── pull_merge.html │ ├── pull_request.html │ ├── recommend.html │ ├── resetpassword.html │ ├── shared │ └── notif_nav.html │ ├── star_repo.html │ ├── stats.html │ ├── timeline.html │ ├── todo.html │ ├── user.html │ ├── user_base.html │ ├── watch_repo.html │ └── watch_user.html ├── thirdparty ├── __init__.py ├── models.py ├── tests.py └── views.py ├── todolist ├── __init__.py ├── models.py ├── tests.py └── views.py ├── urls.py ├── viewtools ├── __init__.py ├── models.py ├── templatetags │ ├── __init__.py │ └── gstools.py ├── tests.py └── views.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .gitignore 3 | env.ini 4 | settings.py 5 | wsgi.py 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gitshell 2 | ======== 3 | Env 4 | ------------ 5 | - Python 2.7 6 | - Django 1.4 7 | - MySQL 8 | - Redis 9 | - Beanstalk 10 | - Memecache 11 | - uwsgi 12 | - Nginx 13 | 14 | Install 15 | ------- 16 | ``` 17 | $ apt-get install automake build-essential flex git libmysqlclient-dev libpcre3 libpcre3-dev libpython2.7 libssl libssl-dev libtool libzip1 libzip-dev mysql mysql-client mysql-server pip python-dev python-pip python-pyasn1 libcurl4-openssl-dev tcl8.5 python-pylibmc python-imaging sudo pip install django-simple-captcha 18 | 19 | $ python setup.py install 20 | $ ... 21 | 22 | $ /opt/bin/nginx start 23 | $ /opt/bin/redis start 24 | $ /opt/bin/beanstalkd start 25 | $ /opt/run/openssh/sbin/sshd 26 | $ /opt/bin/uwsgi stop 8001 && sleep 3 && /opt/bin/uwsgi start 8001 27 | ``` 28 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/__init__.py -------------------------------------------------------------------------------- /daemon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/daemon/__init__.py -------------------------------------------------------------------------------- /daemon/dropbox_sync_repo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os, shutil, sys, json 3 | import random, re, json, time 4 | import httplib, urllib, hashlib 5 | from datetime import datetime 6 | import base64, hashlib, urlparse 7 | from subprocess import Popen 8 | from subprocess import PIPE 9 | from gitshell.settings import logger 10 | 11 | SECRET_KEY = 'git424953shell' 12 | REMOTE_HOST = 'gitshell.com' 13 | REMOTE_PORT = 443 14 | 15 | def start(): 16 | logger.info('==================== START dropbox_sync_repo ====================') 17 | gitshell_connection = None 18 | try: 19 | gitshell_connection = httplib.HTTPSConnection(REMOTE_HOST, REMOTE_PORT, timeout=10) 20 | headers = {'Accept': 'application/json'} 21 | gitshell_connection.request('GET', '/gitshell/list_latest_push_repo/2M/?secret_key='+SECRET_KEY, '', headers) 22 | response = gitshell_connection.getresponse() 23 | if response.status == 200: 24 | json_str = response.read() 25 | json_response = json.loads(json_str) 26 | result = json_response['result'] 27 | repos = json_response['latest_push_repos'] 28 | if result == 'success': 29 | for repo in repos: 30 | do_repo(repo) 31 | except Exception, e: 32 | logger.exception(e) 33 | finally: 34 | if gitshell_connection: gitshell_connection.close() 35 | logger.info('==================== END dropbox_sync_repo ====================') 36 | 37 | def do_repo(repo): 38 | id = str(repo['id']) 39 | username = repo['username'] 40 | name = repo['name'] 41 | dropbox_sync = str(repo['dropbox_sync']) 42 | visibly = str(repo['visibly']) 43 | args = ['/bin/bash', '/opt/bin/dropbox_sync_repo.sh', id, username, name, dropbox_sync, visibly] 44 | popen = Popen(args, stdout=PIPE, shell=False, close_fds=True) 45 | result = popen.communicate()[0].strip() 46 | 47 | if __name__ == '__main__': 48 | start() 49 | 50 | 51 | -------------------------------------------------------------------------------- /daemon/dropbox_sync_repo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . /lib/lsb/init-functions 4 | DIR="$( cd "$( dirname "$0" )" && pwd )" 5 | 6 | if [ $# -lt 5 ] || [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ]; then 7 | exit 128 8 | fi 9 | 10 | LOCAL_REPO_PATH="/home/git/Repositories" 11 | DROPBOX_REPO_PATH="/home/git/Dropbox/Apps/gitshell/repositories" 12 | SERVER_REPO_PATH="/opt/repo/set" 13 | for path in "$LOCAL_REPO_PATH" "$DROPBOX_REPO_PATH"; do 14 | if [ ! -e "$path" ]; then 15 | mkdir -p "$path" 16 | fi 17 | done 18 | 19 | id="$1" 20 | username="$2" 21 | reponame="$3" 22 | dropbox_sync="$4" 23 | visibly="$5" 24 | remote_raw_repo_path="$SERVER_REPO_PATH/${username}/${reponame}.git" 25 | local_sync_repo_path="$LOCAL_REPO_PATH/$id.git" 26 | dropbox_sync_repo_path="$DROPBOX_REPO_PATH/${username}/${id}_${reponame}.git" 27 | 28 | # function git clone bare from remote to specail path 29 | function git_clone_bare { 30 | if [ -z "$1" ]; then 31 | exit 128 32 | fi 33 | sync_repo_path="$1" 34 | sync_repo_dirpath=`dirname $sync_repo_path` 35 | if [ ! -e "$sync_repo_dirpath" ]; then 36 | mkdir -p "$sync_repo_dirpath" 37 | fi 38 | 39 | if [ ! -e "$sync_repo_path" ]; then 40 | git clone --bare --mirror "ssh://git@gitshell.com:2222/$remote_raw_repo_path" "$sync_repo_path" 41 | cd "$sync_repo_path"; git update-server-info; cd - 42 | else 43 | cd "$sync_repo_path"; git fetch; git update-server-info; cd - 44 | fi 45 | } 46 | 47 | # 1 sync to local as backup 48 | if [ "$visibly" == '0' ]; then 49 | git_clone_bare "$local_sync_repo_path" 50 | fi 51 | 52 | # 2 sync to dropbox 53 | if [ "$visibly" == '0' ] && [ "$dropbox_sync" == '1' ]; then 54 | # sync repo to dropbox 55 | git_clone_bare "$dropbox_sync_repo_path" 56 | else 57 | # delete repo if visibly == 1 or dropbox_sync == 0 58 | if [ -e "$dropbox_sync_repo_path" ]; then 59 | rm -rf "$dropbox_sync_repo_path" 60 | fi 61 | fi 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /daemon/eventworker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | cd $DIR; cd ../../ 5 | GITSHELL_DIR=`pwd` 6 | 7 | export PYTHONPATH=$GITSHELL_DIR 8 | export DJANGO_SETTINGS_MODULE=gitshell.settings 9 | 10 | . /lib/lsb/init-functions 11 | 12 | SCRIPT="$GITSHELL_DIR/gitshell/daemon/eventworker.py" 13 | LOG_PATH="/opt/run/var/log" 14 | current=`date +"%s"` 15 | LOG_FILENAME="$LOG_PATH/event.$current.log" 16 | case "$1" in 17 | start) 18 | python "$SCRIPT" start > $LOG_FILENAME 2 >& 1 & 19 | ;; 20 | stop) 21 | python "$SCRIPT" stop 22 | ;; 23 | *) 24 | log_action_msg "Usage: ${SCRIPT} {start|stop}" 25 | exit 1 26 | esac 27 | 28 | -------------------------------------------------------------------------------- /daemon/forkworker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os 3 | import shutil 4 | import sys, json 5 | import beanstalkc 6 | from subprocess import Popen 7 | from subprocess import PIPE 8 | from gitshell.repo.models import RepoManager 9 | from gitshell.gsuser.models import GsuserManager 10 | from gitshell.daemon.models import EventManager, FORK_TUBE_NAME 11 | from gitshell.settings import GIT_BARE_REPO_PATH, BEANSTALK_HOST, BEANSTALK_PORT, logger 12 | from django.db.models.signals import post_save, post_delete 13 | from gitshell.objectscache.da import da_post_save 14 | 15 | def start(): 16 | logger.info('==================== START forkworker ====================') 17 | beanstalk = beanstalkc.Connection(host=BEANSTALK_HOST, port=BEANSTALK_PORT) 18 | EventManager.switch(beanstalk, FORK_TUBE_NAME) 19 | while True: 20 | event_job = beanstalk.reserve() 21 | try: 22 | event = json.loads(event_job.body) 23 | # exit signal 24 | if event['type'] == -1: 25 | event_job.delete() 26 | sys.exit(0) 27 | do_event(event) 28 | except Exception, e: 29 | logger.info('do_event catch except, event: %s' % event_job.body) 30 | logger.exception(e) 31 | event_job.delete() 32 | logger.info('==================== STOP forkworker ====================') 33 | 34 | # git gc and file copy, nothing more 35 | def do_event(event): 36 | from_repo_id = event['from_repo_id'] 37 | to_repo_id = event['to_repo_id'] 38 | from_repo = RepoManager.get_repo_by_id(from_repo_id) 39 | to_repo = RepoManager.get_repo_by_id(to_repo_id) 40 | copy_from_bare = False 41 | if to_repo is None: 42 | return 43 | if from_repo is None: 44 | copy_from_bare = True 45 | from_repo_path = GIT_BARE_REPO_PATH 46 | if not copy_from_bare: 47 | from_repo_path = from_repo.get_abs_repopath() 48 | to_repo_path = to_repo.get_abs_repopath() 49 | if not os.path.exists(from_repo_path): 50 | logger.info('from_repo_path: %s is not exists, clone failed' % from_repo_path) 51 | return 52 | if chdir(from_repo_path) is False: 53 | logger.info('chdir to from_repo_path: %s is False, clone failed' % from_repo_path) 54 | return 55 | if os.path.exists(to_repo_path): 56 | logger.info('to_repo_path: %s already exists, clone failed' % to_repo_path) 57 | return 58 | args = ['/usr/bin/git', 'gc'] 59 | popen = Popen(args, stdout=PIPE, shell=False, close_fds=True) 60 | result = popen.communicate()[0].strip() 61 | to_repo_dirname = os.path.dirname(to_repo_path) 62 | if not os.path.exists(to_repo_dirname): 63 | os.makedirs(to_repo_dirname) 64 | shutil.copytree(from_repo_path, to_repo_path) 65 | update_repo_status(from_repo, to_repo) 66 | 67 | def update_repo_status(from_repo, to_repo): 68 | from_repo.fork = from_repo.fork + 1 69 | from_repo.save() 70 | to_repo.status = 0 71 | to_repo.save() 72 | 73 | def chdir(path): 74 | try: 75 | os.chdir(path) 76 | return True 77 | except Exception, e: 78 | logger.exception(e) 79 | return False 80 | 81 | def stop(): 82 | EventManager.send_stop_event(FORK_TUBE_NAME) 83 | logger.info('send stop event message...') 84 | 85 | def __cache_version_update(sender, **kwargs): 86 | da_post_save(kwargs['instance']) 87 | 88 | if __name__ == '__main__': 89 | post_save.connect(__cache_version_update) 90 | post_delete.connect(__cache_version_update) 91 | if len(sys.argv) < 2: 92 | print 'usage: start|stop' 93 | sys.exit(1) 94 | action = sys.argv[1] 95 | if action == 'start': 96 | start() 97 | elif action == 'stop': 98 | stop() 99 | -------------------------------------------------------------------------------- /daemon/forkworker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | cd $DIR; cd ../../ 5 | GITSHELL_DIR=`pwd` 6 | 7 | export PYTHONPATH=$GITSHELL_DIR 8 | export DJANGO_SETTINGS_MODULE=gitshell.settings 9 | 10 | . /lib/lsb/init-functions 11 | 12 | SCRIPT="$GITSHELL_DIR/gitshell/daemon/forkworker.py" 13 | LOG_PATH="/opt/run/var/log" 14 | current=`date +"%s"` 15 | LOG_FILENAME="$LOG_PATH/forkevent.$current.log" 16 | case "$1" in 17 | start) 18 | python "$SCRIPT" start > $LOG_FILENAME 2 >& 1 & 19 | ;; 20 | stop) 21 | python "$SCRIPT" stop 22 | ;; 23 | *) 24 | log_action_msg "Usage: ${SCRIPT} {start|stop}" 25 | exit 1 26 | esac 27 | -------------------------------------------------------------------------------- /daemon/hookworker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | cd $DIR; cd ../../ 5 | GITSHELL_DIR=`pwd` 6 | 7 | export PYTHONPATH=$GITSHELL_DIR 8 | export DJANGO_SETTINGS_MODULE=gitshell.settings 9 | 10 | . /lib/lsb/init-functions 11 | 12 | SCRIPT="$GITSHELL_DIR/gitshell/daemon/hookworker.py" 13 | LOG_PATH="/opt/run/var/log" 14 | current=`date +"%s"` 15 | LOG_FILENAME="$LOG_PATH/hookworker.$current.log" 16 | case "$1" in 17 | start) 18 | python "$SCRIPT" start > $LOG_FILENAME 2 >& 1 & 19 | ;; 20 | stop) 21 | python "$SCRIPT" stop 22 | ;; 23 | *) 24 | log_action_msg "Usage: ${SCRIPT} {start|stop}" 25 | exit 1 26 | esac 27 | 28 | -------------------------------------------------------------------------------- /daemon/importrepoworker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os, threading 3 | import shutil 4 | import sys, json 5 | import beanstalkc 6 | from subprocess import Popen 7 | from subprocess import PIPE 8 | from gitshell.repo.models import RepoManager 9 | from gitshell.gsuser.models import GsuserManager 10 | from gitshell.daemon.models import EventManager, IMPORT_REPO_TUBE_NAME 11 | from gitshell.settings import GIT_BARE_REPO_PATH, BEANSTALK_HOST, BEANSTALK_PORT, logger 12 | from django.db.models.signals import post_save, post_delete 13 | from gitshell.objectscache.da import da_post_save 14 | 15 | TOTAL_THREAD_COUNT = 10 16 | 17 | def start(): 18 | logger.info('==================== START importrepoworker ====================') 19 | import_repo_threads = [] 20 | for i in range(0, TOTAL_THREAD_COUNT): 21 | import_repo_threads.append(ImportRepoThread()) 22 | for import_repo_thread in import_repo_threads: 23 | import_repo_thread.start() 24 | for import_repo_thread in import_repo_threads: 25 | import_repo_thread.join() 26 | logger.info('==================== STOP importrepoworker ====================') 27 | 28 | def stop(): 29 | for i in range(0, TOTAL_THREAD_COUNT): 30 | EventManager.send_stop_event(IMPORT_REPO_TUBE_NAME) 31 | logger.info('send stop event message...') 32 | 33 | class ImportRepoThread(threading.Thread): 34 | 35 | def run(self): 36 | beanstalk = beanstalkc.Connection(host=BEANSTALK_HOST, port=BEANSTALK_PORT) 37 | EventManager.switch(beanstalk, IMPORT_REPO_TUBE_NAME) 38 | while True: 39 | event_job = beanstalk.reserve() 40 | try: 41 | event = json.loads(event_job.body) 42 | # exit signal 43 | if event['type'] == -1: 44 | event_job.delete() 45 | sys.exit(0) 46 | self._do_event(event) 47 | except Exception, e: 48 | logger.info('do_event catch except, event: %s' % event_job.body) 49 | logger.exception(e) 50 | event_job.delete() 51 | 52 | # import from remote git url, like github 53 | def _do_event(self, event): 54 | username = event['username'] 55 | reponame = event['reponame'] 56 | remote_git_url = event['remote_git_url'] 57 | local_user = GsuserManager.get_user_by_name(username) 58 | local_repo = RepoManager.get_repo_by_name(username, reponame) 59 | if local_user is None or local_repo is None or local_repo.status == 0: 60 | return 61 | local_repo_path = local_repo.get_abs_repopath() 62 | if os.path.exists(local_repo_path): 63 | return 64 | args = ['/bin/bash', '/opt/bin/git-import-remote-repo.sh'] + [local_repo_path, remote_git_url] 65 | try: 66 | popen = Popen(args, stdout=PIPE, shell=False, close_fds=True) 67 | output = popen.communicate()[0].strip() 68 | returncode = popen.returncode 69 | if returncode == 0: 70 | RepoManager.check_export_ok_file(local_repo) 71 | diff_size = long(output) 72 | RepoManager.update_user_repo_quote(local_user, local_repo, diff_size) 73 | local_repo.status = 0 74 | local_repo.save() 75 | else: 76 | local_repo.status = 500 77 | local_repo.save() 78 | except Exception, e: 79 | local_repo.status = 500 80 | local_repo.save() 81 | logger.exception(e) 82 | RepoManager.check_export_ok_file(local_repo) 83 | 84 | def __cache_version_update(sender, **kwargs): 85 | da_post_save(kwargs['instance']) 86 | 87 | if __name__ == '__main__': 88 | post_save.connect(__cache_version_update) 89 | post_delete.connect(__cache_version_update) 90 | if len(sys.argv) < 2: 91 | print 'usage: start|stop' 92 | sys.exit(1) 93 | action = sys.argv[1] 94 | if action == 'start': 95 | start() 96 | elif action == 'stop': 97 | stop() 98 | else: 99 | print 'usage: start|stop' 100 | sys.exit(1) 101 | 102 | -------------------------------------------------------------------------------- /daemon/importrepoworker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | cd $DIR; cd ../../ 5 | GITSHELL_DIR=`pwd` 6 | 7 | export PYTHONPATH=$GITSHELL_DIR 8 | export DJANGO_SETTINGS_MODULE=gitshell.settings 9 | 10 | . /lib/lsb/init-functions 11 | 12 | SCRIPT="$GITSHELL_DIR/gitshell/daemon/importrepoworker.py" 13 | LOG_PATH="/opt/run/var/log" 14 | current=`date +"%s"` 15 | LOG_FILENAME="$LOG_PATH/importrepo.$current.log" 16 | case "$1" in 17 | start) 18 | python "$SCRIPT" start > $LOG_FILENAME 2 >& 1 & 19 | ;; 20 | stop) 21 | python "$SCRIPT" stop 22 | ;; 23 | *) 24 | log_action_msg "Usage: ${SCRIPT} {start|stop}" 25 | exit 1 26 | esac 27 | -------------------------------------------------------------------------------- /daemon/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | import json, beanstalkc 3 | from gitshell.objectscache.models import BaseModel 4 | from gitshell.settings import BEANSTALK_HOST, BEANSTALK_PORT 5 | 6 | EVENT_TUBE_NAME = 'commit_event' 7 | FORK_TUBE_NAME = 'fork_event' 8 | HOOK_TUBE_NAME = 'hook_event' 9 | IMPORT_REPO_TUBE_NAME = 'import_repo_event' 10 | 11 | class EventManager(): 12 | 13 | @classmethod 14 | def sendevent(self, tube, event): 15 | beanstalk = beanstalkc.Connection(host=BEANSTALK_HOST, port=BEANSTALK_PORT) 16 | self.switch(beanstalk, tube) 17 | beanstalk.put(str(event)) 18 | 19 | @classmethod 20 | def send_stop_event(self, tube): 21 | stop_event = {'type': -1} 22 | self.sendevent(tube, json.dumps(stop_event)) 23 | 24 | @classmethod 25 | def switch(self, beanstalk, tube): 26 | beanstalk.use(tube) 27 | beanstalk.watch(tube) 28 | beanstalk.ignore('default') 29 | 30 | # ======== send event ======== 31 | @classmethod 32 | def send_fork_event(self, from_repo_id, to_repo_id): 33 | fork_event = {'type': 0, 'from_repo_id': from_repo_id, 'to_repo_id': to_repo_id} 34 | self.sendevent(FORK_TUBE_NAME, json.dumps(fork_event)) 35 | 36 | @classmethod 37 | def send_import_repo_event(self, username, reponame, remote_git_url): 38 | import_repo_event = {'type': 0, 'username': username, 'reponame': reponame, 'remote_git_url': remote_git_url} 39 | self.sendevent(IMPORT_REPO_TUBE_NAME, json.dumps(import_repo_event)) 40 | 41 | -------------------------------------------------------------------------------- /daemon/notifworker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os, threading, shutil, sys, json, time 3 | import beanstalkc 4 | from datetime import datetime 5 | from subprocess import Popen, PIPE 6 | from gitshell.repo.models import RepoManager 7 | from gitshell.gsuser.models import GsuserManager 8 | from gitshell.daemon.models import EventManager, IMPORT_REPO_TUBE_NAME 9 | from gitshell.settings import GIT_BARE_REPO_PATH, BEANSTALK_HOST, BEANSTALK_PORT, logger 10 | from gitshell.objectscache.da import da_post_save 11 | from gitshell.feed.models import FeedManager 12 | from gitshell.feed.mailUtils import Mailer 13 | from django.db.models.signals import post_save, post_delete 14 | 15 | STOP_FILE_FLAG = '/tmp/notifworker.exit.flag' 16 | TIME_NEVER_COME = datetime(9999, 1, 1, 1, 1) 17 | ROW_COUNT = 5000 18 | 19 | def start(): 20 | logger.info('==================== START notifworker ====================') 21 | while True: 22 | expect_notif_time = datetime.now() 23 | #print expect_notif_time 24 | if os.path.exists(STOP_FILE_FLAG): 25 | os.remove(STOP_FILE_FLAG) 26 | break 27 | for i in range(0, 1000): 28 | notifSettings = FeedManager.list_notifsetting_by_expectNotifTime(expect_notif_time, ROW_COUNT*i, ROW_COUNT) 29 | #print len(notifSettings) 30 | for notifSetting in notifSettings: 31 | #print notifSetting.id 32 | from_time = notifSetting.last_notif_time 33 | to_time = expect_notif_time 34 | #print from_time, to_time 35 | if from_time >= to_time: 36 | notifSetting.expect_notif_time = TIME_NEVER_COME 37 | notifSetting.save() 38 | continue 39 | notifMessages = FeedManager.list_notifmessage_by_userId_betweenTime_notifTypes(notifSetting.user_id, from_time, to_time, notifSetting.notif_types, 0, 1000) 40 | #print len(notifMessages) 41 | _send_notifMessages(notifMessages, notifSetting) 42 | notifSetting.last_notif_time = expect_notif_time 43 | notifSetting.expect_notif_time = TIME_NEVER_COME 44 | notifSetting.save() 45 | if len(notifSettings) < ROW_COUNT: 46 | break 47 | now = int(time.time()) 48 | next_minute_left = 60 - now%60 49 | if next_minute_left == 0: 50 | next_minute_left = 60 51 | #print next_minute_left 52 | time.sleep(next_minute_left) 53 | 54 | logger.info('==================== STOP notifworker ====================') 55 | 56 | def _send_notifMessages(notifMessages, notifSetting): 57 | userprofile = GsuserManager.get_userprofile_by_id(notifSetting.user_id) 58 | header = u'来自Gitshell的 %s 个通知' % len(notifMessages) 59 | html = FeedManager.render_notifMessages_as_html(userprofile, header, notifMessages) 60 | Mailer().send_html_mail(header, html, None, [notifSetting.email]) 61 | 62 | def stop(): 63 | open(STOP_FILE_FLAG, 'a').close() 64 | 65 | def __cache_version_update(sender, **kwargs): 66 | da_post_save(kwargs['instance']) 67 | 68 | if __name__ == '__main__': 69 | post_save.connect(__cache_version_update) 70 | post_delete.connect(__cache_version_update) 71 | if len(sys.argv) < 2: 72 | print 'usage: start|stop' 73 | sys.exit(1) 74 | action = sys.argv[1] 75 | if action == 'start': 76 | start() 77 | elif action == 'stop': 78 | stop() 79 | else: 80 | print 'usage: start|stop' 81 | sys.exit(1) 82 | 83 | 84 | -------------------------------------------------------------------------------- /daemon/notifworker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | cd $DIR; cd ../../ 5 | GITSHELL_DIR=`pwd` 6 | 7 | export PYTHONPATH=$GITSHELL_DIR 8 | export DJANGO_SETTINGS_MODULE=gitshell.settings 9 | 10 | . /lib/lsb/init-functions 11 | 12 | SCRIPT="$GITSHELL_DIR/gitshell/daemon/notifworker.py" 13 | LOG_PATH="/opt/run/var/log" 14 | current=`date +"%s"` 15 | LOG_FILENAME="$LOG_PATH/notif.$current.log" 16 | case "$1" in 17 | start) 18 | python "$SCRIPT" start > $LOG_FILENAME 2 >& 1 & 19 | ;; 20 | stop) 21 | python "$SCRIPT" stop 22 | ;; 23 | *) 24 | log_action_msg "Usage: ${SCRIPT} {start|stop}" 25 | exit 1 26 | esac 27 | 28 | -------------------------------------------------------------------------------- /daemon/statsworker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import json 3 | import beanstalkc 4 | 5 | def main(): 6 | beanstalk = beanstalkc.Connection(host='localhost', port=11300) 7 | beanstalk.use('stats_event') 8 | exit_flag = False 9 | while not exit_flag: 10 | event_job = beanstalk.reserve() 11 | try: 12 | do_stats_event(event_job.body) 13 | except Exception, e: 14 | print 'do_event catch except, event: %s' % event_job.body 15 | print 'exception: %s' % e 16 | event_job.delete() 17 | 18 | def do_stats_event(event_job): 19 | pass 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /daemon/test_redis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import redis 4 | import random 5 | 6 | def main(): 7 | feed_redis = redis.Redis('localhost', 6379, 3) 8 | for i in range(0, 1000): 9 | for ftype in ['r', 'u', 'wu', 'bwu', 'wr', 'c']: 10 | key = '%s:%s' % (ftype, i + 10000) 11 | for j in range(0, 100): 12 | value = random.randint(0, 1000000) 13 | feed_redis.zadd(key, value, value+1) 14 | 15 | if __name__ == '__main__': 16 | main() 17 | -------------------------------------------------------------------------------- /daemon/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /daemon/triggerworker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | def main(): 4 | pass 5 | 6 | if __name__ == '__main__': 7 | main() 8 | -------------------------------------------------------------------------------- /daemon/userstatsjob.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os, threading, shutil, sys, json, re 3 | from django.contrib.auth.models import User, UserManager 4 | from gitshell.repo.models import RepoManager, CommitHistory 5 | from gitshell.issue.models import IssueManager, Issue 6 | from gitshell.gsuser.models import GsuserManager, Userprofile 7 | from gitshell.keyauth.models import KeyauthManager 8 | 9 | def start(): 10 | users = User.objects.all() 11 | for user in users: 12 | userprofile = GsuserManager.get_userprofile_by_id(user.id) 13 | if not userprofile: 14 | continue 15 | if not re.match('[a-zA-Z0-9-_]+', user.username): 16 | continue 17 | userstats = {} 18 | userstats['username'] = user.username 19 | userstats['email'] = user.email 20 | userstats['date_joined'] = user.date_joined.strftime('%Y/%m/%d %H:%M:%S') 21 | userstats['last_login'] = user.last_login.strftime('%Y/%m/%d %H:%M:%S') 22 | repos = RepoManager.list_repo_by_userId(user.id, 0, 1000) 23 | userstats['repo_total_count'] = len(repos) 24 | first_time_commit = None; last_time_commit = None; commits = 0; forks = 0; repo_private_count = 0 25 | for repo in repos: 26 | commits = commits + repo.commit 27 | if repo.auth_type != 0: 28 | repo_private_count = repo_private_count + 1 29 | if repo.fork_repo_id != 0: 30 | forks = forks + 1 31 | commitHistorys = CommitHistory.objects.filter(visibly=0).filter(repo_id=repo.id).order_by('create_time')[0:1] 32 | if len(commitHistorys) > 0: 33 | first_time_commitHistory = commitHistorys[0] 34 | if first_time_commit is None or first_time_commit > first_time_commitHistory.create_time: 35 | first_time_commit = first_time_commitHistory.create_time 36 | commitHistorys = CommitHistory.objects.filter(visibly=0).filter(repo_id=repo.id).order_by('-create_time')[0:1] 37 | if len(commitHistorys) > 0: 38 | last_time_commitHistory = commitHistorys[0] 39 | if last_time_commit is None or last_time_commit < last_time_commitHistory.create_time: 40 | last_time_commit = last_time_commitHistory.create_time 41 | userstats['repo_private_count'] = repo_private_count 42 | if first_time_commit: 43 | userstats['first_time_commit'] = first_time_commit.strftime('%Y/%m/%d %H:%M:%S') 44 | else: 45 | userstats['first_time_commit'] = '' 46 | if last_time_commit: 47 | userstats['last_time_commit'] = last_time_commit.strftime('%Y/%m/%d %H:%M:%S') 48 | else: 49 | userstats['last_time_commit'] = '' 50 | userstats['commits'] = commits 51 | userstats['watch_repo'] = userprofile.watchrepo 52 | userstats['fork_repo'] = forks 53 | pullrequests = RepoManager.list_pullRequest_by_pullUserId(user.id) 54 | userstats['pullrequests'] = len(pullrequests) 55 | issues = Issue.objects.filter(visibly=0).filter(creator_user_id=user.id)[0:1000] 56 | userstats['issues'] = len(issues) 57 | userpubkeys = KeyauthManager.list_userpubkey_by_userId(user.id) 58 | userstats['ssh_key'] = len(userpubkeys) 59 | csv_items = [userstats['username'], userstats['email'], userstats['date_joined'], userstats['last_login'], str(int(userstats['repo_total_count'])), str(int(userstats['repo_private_count'])), userstats['first_time_commit'], userstats['last_time_commit'], str(int(userstats['commits'])), str(int(userstats['watch_repo'])), str(int(userstats['fork_repo'])), str(int(userstats['pullrequests'])), str(int(userstats['issues'])), str(int(userstats['ssh_key']))] 60 | print ','.join(csv_items) 61 | 62 | if __name__ == '__main__': 63 | if len(sys.argv) < 2: 64 | print 'usage: start|stop' 65 | sys.exit(1) 66 | action = sys.argv[1] 67 | if action == 'start': 68 | start() 69 | elif action == 'stop': 70 | stop() 71 | else: 72 | print 'usage: start|stop' 73 | sys.exit(1) 74 | 75 | -------------------------------------------------------------------------------- /daemon/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /dist/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/dist/__init__.py -------------------------------------------------------------------------------- /dist/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | -------------------------------------------------------------------------------- /dist/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /dist/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse, HttpResponseRedirect, Http404 2 | from django.core.cache import cache 3 | import re 4 | 5 | def repo(request, username, reponame): 6 | repo_partition = get_repo_partition(username, reponame) 7 | if repo_partition is not None: 8 | return HttpResponse(repo_partition.host + ' ' + repo_root, content_type="text/plain") 9 | return HttpResponse("auth and distributed", content_type="text/plain") 10 | 11 | def refresh(request): 12 | do_refresh() 13 | return echo(request) 14 | 15 | def echo(request): 16 | global repo_partition_array 17 | str_list = [] 18 | for repo_partition in repo_partition_array: 19 | str_list.append(str(repo_partition)) 20 | return HttpResponse('\n'.join(str_list), content_type="text/plain") 21 | 22 | def get_repo_partition(username, reponame): 23 | global repo_partition_array 24 | if len(repo_partition_array) == 1: 25 | return repo_partition_array[0] 26 | hashcode = abs(hash(username) % 1024) 27 | for repo_partition in repo_partition_array: 28 | if hashcode >= repo_partition.from_index and hashcode <= repo_partition.to_index: 29 | return repo_partition 30 | return None 31 | 32 | def do_refresh(): 33 | global repo_partition_array 34 | new_repo_partition_array = [] 35 | file = open(repo_partition_conf_file, 'r') 36 | try: 37 | for line in file: 38 | array = re.sub("\s+", " ", line.strip()).split(" ") 39 | if len(array) != 3 or not re.match("\d+", array[0]) or not re.match("\d+", array[1]): 40 | continue 41 | repo_partition = RepoPartition(int(array[0]), int(array[1]), array[2]) 42 | new_repo_partition_array.append(repo_partition) 43 | finally: 44 | file.close() 45 | if len(new_repo_partition_array) > 0: 46 | repo_partition_array = new_repo_partition_array 47 | 48 | # static field and class 49 | repo_root = '/opt/repo/' 50 | repo_partition_array = [] 51 | repo_partition_conf_file = "/opt/run/var/repo_partition.conf" 52 | 53 | class RepoPartition: 54 | def __init__(self, from_index, to_index, host): 55 | self.from_index = from_index 56 | self.to_index = to_index 57 | self.host = host 58 | def __str__(self): 59 | return ' '.join([str(self.from_index), str(self.to_index), self.host]) 60 | 61 | #do_refresh() 62 | -------------------------------------------------------------------------------- /explore/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/explore/__init__.py -------------------------------------------------------------------------------- /explore/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /explore/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /explore/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from datetime import datetime 3 | from datetime import timedelta 4 | from django.http import HttpResponse, HttpResponseRedirect, Http404 5 | from django.template import RequestContext 6 | from django.shortcuts import render_to_response 7 | from django.contrib.auth.models import User 8 | from django.contrib.auth.decorators import login_required 9 | from gitshell.repo.models import RepoManager 10 | from gitshell.stats import timeutils 11 | from gitshell.stats.models import StatsManager 12 | from gitshell.gsuser.models import GsuserManager 13 | from gitshell.feed.feed import FeedAction 14 | from gitshell.feed.views import latest_feeds_as_json 15 | 16 | def explore(request): 17 | repo_ids = get_hot_repo_ids() 18 | raw_repos = RepoManager.list_repo_by_ids(repo_ids) 19 | repos = [x for x in raw_repos if x.auth_type != 2] 20 | user_ids = [x.user_id for x in repos] 21 | users_dict = GsuserManager.map_users(user_ids) 22 | username_dict = dict([(users_dict[x]['id'], users_dict[x]['username']) for x in users_dict]) 23 | userimgurl_dict = dict([(users_dict[x]['id'], users_dict[x]['imgurl']) for x in users_dict]) 24 | 25 | feedAction = FeedAction() 26 | latest_feeds = feedAction.get_latest_feeds(0, 100) 27 | feeds_as_json = latest_feeds_as_json(request, latest_feeds) 28 | 29 | response_dictionary = {'repos': repos, 'users_dict': users_dict, 'username_dict': username_dict, 'userimgurl_dict': userimgurl_dict, 'feeds_as_json': feeds_as_json} 30 | return render_to_response('explore/explore.html', 31 | response_dictionary, 32 | context_instance=RequestContext(request)) 33 | 34 | random_page = 0 35 | max_page = 1 36 | def get_hot_repo_ids(): 37 | global random_page 38 | random_page = random_page + 1 39 | day_page = random_page % max_page 40 | week_page = (day_page + 1) % max_page 41 | month_page = (week_page + 1) % max_page 42 | now = datetime.now() 43 | round_day = timeutils.get_round_day(now) 44 | round_week = timeutils.get_round_week(now) 45 | round_month = timeutils.get_round_month(now) 46 | day_stats_repo = StatsManager.list_allrepo_stats('day', round_day, 20*day_page, 20*(day_page+1)) 47 | week_stats_repo = StatsManager.list_allrepo_stats('week', round_week, 20*week_page, 20*(week_page+1)) 48 | month_stats_repo = StatsManager.list_allrepo_stats('month', round_month, 20*month_page, 20*(month_page+1)) 49 | uniq_repo_dict = {} 50 | uniq_repo_ids = [] 51 | for statsrepo in (day_stats_repo + week_stats_repo + month_stats_repo): 52 | if statsrepo.repo_id not in uniq_repo_dict: 53 | uniq_repo_dict[statsrepo.repo_id] = 1 54 | uniq_repo_ids.append(statsrepo.repo_id) 55 | return uniq_repo_ids 56 | 57 | -------------------------------------------------------------------------------- /feed/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/feed/__init__.py -------------------------------------------------------------------------------- /feed/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /githooks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/githooks/__init__.py -------------------------------------------------------------------------------- /githooks/diff-tree-blob-size.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | if [ $# -lt 4 ]; then 5 | exit 1 6 | fi 7 | 8 | args=("$@") 9 | # abspath is the repos path 10 | abspath=${args[0]} 11 | cd "$abspath" 12 | 13 | index=1 14 | size=0 15 | du_size=0 16 | tempfile=`mktemp` 17 | 18 | while [ $index -lt $# ]; do 19 | oldrev="${args[$index]}" 20 | let index=($index+1) 21 | newrev="${args[$index]}" 22 | let index=($index+1) 23 | refname="${args[$index]}" 24 | let index=($index+1) 25 | if [ -z "$oldrev" ] || [ -z "$newrev" ] || [ -z "$refname" ]; then 26 | continue 27 | fi 28 | if [ "$oldrev" != '0000000000000000000000000000000000000000' ]; then 29 | /usr/bin/git diff-tree --raw -r -c -M -C --no-commit-id "$oldrev" "$newrev" | head -n 100 | awk '{print $4}' > $tempfile 30 | else 31 | /usr/bin/git diff-tree --raw -r -c -M -C --no-commit-id "$newrev" | head -n 100 | awk '{print $4}' > $tempfile 32 | fi 33 | total_line=`wc -l $tempfile | awk '{print $1}'` 34 | if [ $total_line -ge 100 ]; then 35 | du_size=`du -sb .` 36 | break 37 | fi 38 | refsize=`/usr/bin/git cat-file --batch-check < $tempfile | awk 'BEGIN{count=0}{count+=$3}END{print count}'` 39 | let size=($size+$refsize) 40 | done 41 | 42 | rm $tempfile 43 | 44 | if [ "$du_size" -eq 0 ]; then 45 | echo "+$size" 46 | else 47 | echo "$du_size" 48 | fi 49 | -------------------------------------------------------------------------------- /githooks/git-import-remote-repo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | ulimit -f 327680 ; ulimit -m 1048576 ; ulimit -v 1048576 5 | 6 | local_repo_path="$1" 7 | remote_git_url="$2" 8 | 9 | /usr/bin/git clone -q --bare "$remote_git_url" "$local_repo_path" 10 | if [ $? == 0 ]; then 11 | rm "$local_repo_path/hooks/"* 12 | cp /opt/repo/gitbare/hooks/* "$local_repo_path/hooks/" 13 | chmod +x "$local_repo_path/hooks/"* 14 | cd "$local_repo_path" 15 | /usr/bin/git update-server-info 16 | dusb=`du -sb $local_repo_path | awk '{print $1}'` 17 | echo -n "$dusb" 18 | exit 0 19 | fi 20 | echo -n "0" 21 | exit 128 22 | -------------------------------------------------------------------------------- /githooks/git-pretty-diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | cd $1 5 | if [ "$2" == '0000000000000000000000000000000000000000' ]; then 6 | git diff --stat $3 7 | echo '-----------------------' 8 | git diff $3 9 | else 10 | git diff --stat $2..$3 11 | echo '-----------------------' 12 | git diff $2..$3 13 | fi 14 | -------------------------------------------------------------------------------- /githooks/git-pretty-log.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | cd $1 5 | if [ "$3" == '0000000000000000000000000000000000000000' ]; then 6 | return '' 7 | fi 8 | if [ "$2" == '0000000000000000000000000000000000000000' ]; then 9 | git log -100 --pretty='%h______%p______%t______%an______%cn______%ct______%ce______%ae______%s' $3 10 | else 11 | git log -100 --pretty='%h______%p______%t______%an______%cn______%ct______%ce______%ae______%s' $2..$3 12 | fi 13 | -------------------------------------------------------------------------------- /githooks/git-pullrequest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 6 ]; then 4 | exit 128 5 | fi 6 | 7 | pullrequest_repo_path="$1" 8 | source_abs_repopath="$2" 9 | source_remote_name="$3" 10 | dest_abs_repopath="$4" 11 | desc_remote_name="$5" 12 | action="$6" 13 | 14 | function prepare { 15 | if [ ! -e "$pullrequest_repo_path" ]; then 16 | mkdir "$pullrequest_repo_path" 17 | fi 18 | 19 | if [ ! -e "$pullrequest_repo_path/.git" ]; then 20 | git init $pullrequest_repo_path 21 | fi 22 | 23 | cd $pullrequest_repo_path 24 | 25 | git ls-remote "$source_remote_name" 26 | if [ $? != 0 ]; then 27 | git remote add "$source_remote_name" "file://$source_abs_repopath" 28 | fi 29 | git ls-remote "$desc_remote_name" 30 | if [ $? != 0 ]; then 31 | git remote add "$desc_remote_name" "file://$dest_abs_repopath" 32 | fi 33 | git fetch "$source_remote_name" 34 | git fetch "$desc_remote_name" 35 | } 36 | 37 | merge_result=128 38 | function merge { 39 | git checkout master 40 | git show-branch $local_source_refs 41 | if [ $? == 0 ]; then 42 | git branch -D $local_source_refs 43 | fi 44 | git show-branch $local_desc_refs 45 | if [ $? == 0 ]; then 46 | git branch -D $local_desc_refs 47 | fi 48 | git branch $local_source_refs $source_remote_name/$source_refs 49 | git branch $local_desc_refs $desc_remote_name/$desc_refs 50 | git checkout $local_desc_refs 51 | echo '----------- starting merge -----------' 52 | git merge $local_source_refs -m "$pullrequest_commit_message" 53 | if [ $? == 0 ]; then 54 | git push $desc_remote_name $local_desc_refs:$desc_refs 55 | if [ $? == 0 ]; then 56 | merge_result=0 57 | fi 58 | else 59 | git merge --abort 60 | fi 61 | exit $merge_result 62 | } 63 | 64 | case "$action" in 65 | prepare) 66 | prepare 67 | ;; 68 | merge) 69 | prepare 70 | if [ $# -lt 9 ]; then 71 | exit 128 72 | fi 73 | source_refs="$7" 74 | desc_refs="$8" 75 | pullrequest_commit_message="$9" 76 | local_source_refs="${source_remote_name}-${source_refs}" 77 | local_desc_refs="${desc_remote_name}-$desc_refs" 78 | merge 79 | ;; 80 | *) 81 | exit 128 82 | esac 83 | 84 | 85 | -------------------------------------------------------------------------------- /githooks/gitbare/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /githooks/gitbare/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /githooks/gitbare/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /githooks/gitbare/hooks/post-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os, sys, time, json, beanstalkc 3 | 4 | def main(): 5 | abspath = os.path.abspath(os.path.dirname(__file__)) 6 | rev_ref_arr = [] 7 | max_line_count = 50 8 | for line in sys.stdin.xreadlines(): 9 | max_line_count = max_line_count - 1 10 | if max_line_count < 0: 11 | break 12 | rev_ref_arr.append(line.strip().split(' ')) 13 | remote_user = get_remote_user() 14 | json_map = {} 15 | json_map['type'] = 0 16 | json_map['remote_user'] = remote_user 17 | json_map['push_timestamp'] = long(time.time()) 18 | json_map['abspath'] = abspath 19 | json_map['revrefarr'] = rev_ref_arr 20 | event = json.dumps(json_map) 21 | send_commit_event(event) 22 | 23 | def get_remote_user(): 24 | if 'REMOTE_USER' in os.environ: 25 | return os.environ['REMOTE_USER'] 26 | return '' 27 | 28 | def switch(beanstalk, tube): 29 | beanstalk.use(tube) 30 | beanstalk.watch(tube) 31 | beanstalk.ignore('default') 32 | 33 | def send_commit_event(event): 34 | beanstalk = beanstalkc.Connection(host='localhost', port=11300) 35 | switch(beanstalk, 'commit_event') 36 | beanstalk.put(event) 37 | 38 | if __name__ == '__main__': 39 | main() 40 | 41 | -------------------------------------------------------------------------------- /githooks/gitbare/hooks/post-update: -------------------------------------------------------------------------------- 1 | exec git update-server-info 2 | -------------------------------------------------------------------------------- /githooks/gitbare/hooks/pre-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import re, sys 3 | 4 | def main(): 5 | pre_check_failed_msg = '' 6 | max_line_count = 50 7 | for line in sys.stdin.xreadlines(): 8 | max_line_count = max_line_count - 1 9 | if max_line_count < 0: 10 | pre_check_failed_msg = 'too many branches or tabs commit, pre-receive failed!' 11 | break 12 | if len(line) > 1024: 13 | pre_check_failed_msg = 'commit message line max 1024!' 14 | break 15 | line_array = line.strip().split(' ') 16 | if len(line_array) != 3: 17 | pre_check_failed_msg = 'git branch or tag name must match ^[a-zA-Z0-9_\.\-]+$' 18 | break 19 | if not (re.match('^\w+$', line_array[0]) and re.match('^\w+$', line_array[1]) and re.match('^[\w\.\-/]+$', line_array[2])): 20 | pre_check_failed_msg = 'illegal branch name!' 21 | break 22 | if pre_check_failed_msg != '': 23 | print pre_check_failed_msg 24 | return 1 25 | return 0 26 | 27 | if __name__ == '__main__': 28 | sys.exit(main()) 29 | -------------------------------------------------------------------------------- /githooks/gitbare/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /githooks/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /githooks/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /githooks/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import os 4 | 5 | #puts "Enforcing Policies... \n(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})" 6 | def main(): 7 | refname = sys.argv[1] 8 | oldrev = sys.argv[2] 9 | newrev = sys.argv[3] 10 | f = open('/tmp/update.hook.log', 'a') 11 | try: 12 | f.write( '%s,%s,%s\n' % ( refname, oldrev, newrev) ) 13 | finally: 14 | f.close() 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /githooks/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /gssettings/Form.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django import forms 3 | from captcha.fields import CaptchaField 4 | from gitshell.gsuser.models import Userprofile 5 | 6 | class UserprofileForm(forms.ModelForm): 7 | class Meta: 8 | model = Userprofile 9 | fields = ('nickname', 'company', 'website', 'location', 'tweet', 'resume') 10 | widgets = { 11 | 'resume': forms.Textarea(attrs={'cols': 50, 'rows': 5}), 12 | } 13 | 14 | def __init__(self, *args, **kwargs): 15 | super(UserprofileForm, self).__init__(*args, **kwargs) 16 | for key, field in self.fields.iteritems(): 17 | self.fields[key].required = False 18 | 19 | class TeamprofileForm(forms.ModelForm): 20 | class Meta: 21 | model = Userprofile 22 | fields = ('username', 'tweet', 'nickname', 'company', 'website') 23 | 24 | def __init__(self, *args, **kwargs): 25 | super(TeamprofileForm, self).__init__(*args, **kwargs) 26 | for key, field in self.fields.iteritems(): 27 | self.fields[key].required = False 28 | 29 | class DoSshpubkeyForm(forms.Form): 30 | pubkey_id = forms.IntegerField(widget=forms.TextInput(attrs={'id': 'pubkeyId'})) 31 | action = forms.CharField(max_length=12,widget=forms.TextInput(attrs={'id':'action'})) 32 | 33 | class SshpubkeyForm(forms.Form): 34 | pubkey_name = forms.CharField(max_length=12,widget=forms.TextInput(attrs={'id': 'pubkeyName'})) 35 | pubkey = forms.CharField(max_length=1024, widget=forms.Textarea(attrs={'cols': 50, 'rows': 5, 'id': 'pubkey'})) 36 | 37 | class ChangepasswordForm(forms.Form): 38 | password = forms.CharField(max_length=64, widget=forms.PasswordInput(render_value=False)) 39 | -------------------------------------------------------------------------------- /gssettings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/gssettings/__init__.py -------------------------------------------------------------------------------- /gssettings/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /gssettings/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /gsuser/Forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from captcha.fields import CaptchaField 3 | 4 | class LoginForm(forms.Form): 5 | email = forms.EmailField(max_length=64, widget=forms.TextInput(attrs={'id': 'email'})) 6 | password = forms.CharField(max_length=64, widget=forms.PasswordInput(render_value=False, attrs={'id': 'password'})) 7 | #captcha = CaptchaField() 8 | rememberme = forms.BooleanField(required=False) 9 | 10 | class JoinForm(forms.Form): 11 | email = forms.EmailField(widget=forms.TextInput(attrs={'id': 'email'})) 12 | username = forms.CharField(max_length=24, widget=forms.TextInput(attrs={'id': 'username'})) 13 | password = forms.CharField(max_length=64, widget=forms.PasswordInput(render_value=False, attrs={'id': 'password'})) 14 | ref_hash = forms.CharField(required=False) 15 | #captcha = CaptchaField() 16 | 17 | class ResetpasswordForm0(forms.Form): 18 | email = forms.EmailField() 19 | 20 | class ResetpasswordForm1(forms.Form): 21 | password = forms.CharField(max_length=64, widget=forms.PasswordInput(render_value=False, attrs={'id': 'password'})) 22 | 23 | # skills and recommends 24 | class SkillsForm(forms.Form): 25 | sid = forms.IntegerField() 26 | skills = forms.CharField(max_length=10) 27 | uid = forms.IntegerField() 28 | action = forms.CharField(max_length=3) 29 | 30 | class RecommendsForm(forms.Form): 31 | content = forms.CharField(max_length=64) 32 | 33 | -------------------------------------------------------------------------------- /gsuser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/gsuser/__init__.py -------------------------------------------------------------------------------- /gsuser/decorators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.http import Http404 3 | from django.http import HttpResponseRedirect 4 | from django.utils.http import urlquote 5 | from gitshell.feed.feed import FeedAction 6 | from gitshell.help.views import error_with_reason 7 | from gitshell.repo.models import RepoManager, REPO_PERMISSION 8 | from gitshell.viewtools.views import json_httpResponse, json_success, json_failed 9 | 10 | def repo_view_permission_check(function): 11 | 12 | def wrap(request, *args, **kwargs): 13 | if len(args) >= 2: 14 | user_name = args[0]; repo_name = args[1] 15 | repo = RepoManager.get_repo_by_name(user_name, repo_name) 16 | if repo is None: 17 | return error_with_reason(request, 'repo_not_found') 18 | if repo.auth_type == 2 and not request.user.is_authenticated(): 19 | return HttpResponseRedirect('/login/?next=' + urlquote(request.path)) 20 | # half private, code is keep 21 | is_allowed_access_repo = RepoManager.is_allowed_access_repo(repo, request.user, REPO_PERMISSION.WEB_VIEW) 22 | if not is_allowed_access_repo: 23 | if request.method == 'POST': 24 | return json_failed(403, u'没有管理权限') 25 | return error_with_reason(request, 'repo_permission_denied') 26 | if request.user.is_authenticated(): 27 | feedAction = FeedAction() 28 | feedAction.add_recently_view_repo_now(request.user.id, repo.id) 29 | return function(request, *args, **kwargs) 30 | wrap.__doc__=function.__doc__ 31 | wrap.__name__=function.__name__ 32 | 33 | return wrap 34 | 35 | def repo_admin_permission_check(function): 36 | 37 | def wrap(request, *args, **kwargs): 38 | if len(args) >= 2: 39 | user_name = args[0]; repo_name = args[1] 40 | repo = RepoManager.get_repo_by_name(user_name, repo_name) 41 | if repo is None: 42 | return error_with_reason(request, 'repo_not_found') 43 | if not request.user.is_authenticated(): 44 | return HttpResponseRedirect('/login/?next=' + urlquote(request.path)) 45 | from gitshell.team.models import TeamManager 46 | user_permission = TeamManager.get_repo_user_permission(repo, request.user) 47 | if user_permission < REPO_PERMISSION.ADMIN: 48 | if user_permission >= REPO_PERMISSION.WEB_VIEW: 49 | return HttpResponseRedirect('/%s/%s/' % (user_name, repo_name)) 50 | if request.method == 'POST': 51 | return json_failed(403, u'没有管理权限') 52 | return error_with_reason(request, 'repo_permission_denied') 53 | if request.user.is_authenticated(): 54 | feedAction = FeedAction() 55 | feedAction.add_recently_view_repo_now(request.user.id, repo.id) 56 | return function(request, *args, **kwargs) 57 | wrap.__doc__=function.__doc__ 58 | wrap.__name__=function.__name__ 59 | 60 | return wrap 61 | 62 | def repo_source_permission_check(function): 63 | 64 | def wrap(request, *args, **kwargs): 65 | if len(args) >= 2: 66 | user_name = args[0]; repo_name = args[1] 67 | repo = RepoManager.get_repo_by_name(user_name, repo_name) 68 | if repo is None: 69 | return error_with_reason(request, 'repo_not_found') 70 | if repo.auth_type != 0 and not request.user.is_authenticated(): 71 | return HttpResponseRedirect('/login/?next=' + urlquote(request.path)) 72 | is_allowed_access_repo = RepoManager.is_allowed_access_repo(repo, request.user, REPO_PERMISSION.READ_ONLY) 73 | if not is_allowed_access_repo: 74 | if request.method == 'POST': 75 | return json_failed(403, u'没有管理权限') 76 | return error_with_reason(request, 'repo_permission_denied') 77 | if request.user.is_authenticated(): 78 | feedAction = FeedAction() 79 | feedAction.add_recently_view_repo_now(request.user.id, repo.id) 80 | return function(request, *args, **kwargs) 81 | wrap.__doc__=function.__doc__ 82 | wrap.__name__=function.__name__ 83 | 84 | return wrap 85 | 86 | -------------------------------------------------------------------------------- /gsuser/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /gsuser/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os, re, pipes, sys, traceback, codecs 3 | import time, json, hashlib, shutil 4 | from django.core.cache import cache 5 | from gitshell.objectscache.models import CacheKey 6 | from gitshell.gsuser.models import GsuserManager 7 | from gitshell.settings import logger 8 | 9 | class UrlRouter(): 10 | 11 | def __init__(self, userprofile, context_username): 12 | self.userprofile = userprofile 13 | self.context_username = context_username 14 | 15 | def route(self, url): 16 | if not self.userprofile or not self.userprofile.id or not self.userprofile.current_user_id: 17 | return url 18 | if self.userprofile.current_user_id == 0 or self.userprofile.id == self.userprofile.current_user_id: 19 | return url 20 | current_user = GsuserManager.get_user_by_id(self.userprofile.current_user_id) 21 | if current_user: 22 | return '/%s/-%s' % (current_user.username, url) 23 | return url 24 | 25 | def route_context(self, url): 26 | if self.context_username and self.context_username != '': 27 | return '/%s/-%s' % (self.context_username, url) 28 | return url 29 | 30 | -------------------------------------------------------------------------------- /help/Forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from captcha.fields import CaptchaField 3 | 4 | class ResetAccessLimitForm(forms.Form): 5 | captcha = CaptchaField() 6 | -------------------------------------------------------------------------------- /help/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/help/__init__.py -------------------------------------------------------------------------------- /help/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /help/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /help/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.core.cache import cache 3 | from django.http import HttpResponse, HttpResponseRedirect, Http404 4 | from django.template import RequestContext 5 | from django.shortcuts import render_to_response 6 | from django.views.decorators.http import require_http_methods 7 | from gitshell.help.Forms import ResetAccessLimitForm 8 | from gitshell.gsuser.middleware import ACL_KEY, ACCESS_WITH_IN_TIME 9 | from gitshell.gsuser.utils import UrlRouter 10 | 11 | def default(request): 12 | title = u'帮助' 13 | response_dictionary = {'title': title} 14 | return render_to_response('help/default.html', 15 | response_dictionary, 16 | context_instance=RequestContext(request)) 17 | 18 | def quickstart(request): 19 | title = u'快速入门' 20 | response_dictionary = {'title': title} 21 | return render_to_response('help/default.html', 22 | response_dictionary, 23 | context_instance=RequestContext(request)) 24 | 25 | def error(request): 26 | title = u'抱歉,错误信息' 27 | response_dictionary = {'title': title} 28 | return render_to_response('help/error.html', 29 | response_dictionary, 30 | context_instance=RequestContext(request)) 31 | 32 | def error_with_reason(request, reason): 33 | title = reason 34 | response_dictionary = {'title': title, 'reason': reason} 35 | return render_to_response('help/error_with_reason.html', 36 | response_dictionary, 37 | context_instance=RequestContext(request)) 38 | 39 | def access_out_of_limit(request): 40 | title = u'访问超出限额' 41 | resetAccessLimitForm = ResetAccessLimitForm() 42 | response_dictionary = {'title': title, 'resetAccessLimitForm': resetAccessLimitForm} 43 | return render_to_response('help/access_out_of_limit.html', 44 | response_dictionary, 45 | context_instance=RequestContext(request)) 46 | 47 | @require_http_methods(["POST"]) 48 | def reset_access_limit(request): 49 | resetAccessLimitForm = ResetAccessLimitForm(request.POST) 50 | if resetAccessLimitForm.is_valid() and request.user.is_authenticated(): 51 | user_id = request.user.id 52 | key = '%s:%s' % (ACL_KEY, user_id) 53 | cache.set(key, 1, ACCESS_WITH_IN_TIME) 54 | return HttpResponseRedirect(request.urlRouter.route('/dashboard/')) 55 | 56 | -------------------------------------------------------------------------------- /issue/Forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django import forms 3 | from django.forms.widgets import RadioSelect, CheckboxSelectMultiple 4 | from gitshell.issue.models import Issue, IssueComment 5 | from gitshell.repo.models import RepoManager 6 | from gitshell.gsuser.models import GsuserManager 7 | 8 | TRACKER_CHOICES = (('1', '缺陷'), ('2', '功能'), ('3', '支持')) 9 | STATUS_CHOICES = (('1', '新建'), ('2', '已指派'), ('3', '进行中'), ('4', '已解决'), ('5', '已关闭'), ('6', '已拒绝')) 10 | PRIORITY_CHOICES = (('1', '紧急'), ('2', '高'), ('3', '普通'), ('4', '低')) 11 | ASSIGNED_CHOICES = () 12 | 13 | class IssueForm(forms.ModelForm): 14 | 15 | category = forms.CharField(required=False) 16 | content = forms.CharField(widget=forms.Textarea, required=False) 17 | 18 | def fill_assigned(self, repo): 19 | if repo is None: 20 | return 21 | memberUsers = RepoManager.list_repo_team_memberUser(repo.id) 22 | self.fields['assigned'] = forms.ChoiceField(choices=[ (o.id, o.username) for o in memberUsers ]) 23 | 24 | class Meta: 25 | model = Issue 26 | fields = ('subject', 'tracker', 'status', 'assigned', 'priority', 'category', 'content',) 27 | widgets = { 28 | 'content': forms.Textarea(attrs={'maxlength': 1024}), 29 | 'assigned': forms.Select(choices=ASSIGNED_CHOICES), 30 | 'tracker': forms.Select(choices=TRACKER_CHOICES), 31 | 'status': forms.Select(choices=STATUS_CHOICES), 32 | 'priority': forms.Select(choices=PRIORITY_CHOICES), 33 | } 34 | 35 | class IssueCommentForm(forms.ModelForm): 36 | class Meta: 37 | model = IssueComment 38 | fields = ('content',) 39 | widgets = { 40 | 'content': forms.Textarea(attrs={'maxlength': 1024}), 41 | } 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /issue/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/issue/__init__.py -------------------------------------------------------------------------------- /issue/cons.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import time 4 | from gitshell.gsuser.models import GsuserManager 5 | from gitshell.repo.models import RepoManager 6 | from django.forms.models import model_to_dict 7 | 8 | class IssueAttrs(): 9 | 10 | def __init__(self, name, value): 11 | self.name = name 12 | self.value = value 13 | 14 | TRACKERS = [IssueAttrs('所有', 0), IssueAttrs('缺陷', 1), IssueAttrs('功能', 2), IssueAttrs('支持', 3)] 15 | STATUSES = [IssueAttrs('所有', 0), IssueAttrs('新建', 1), IssueAttrs('已指派', 2), IssueAttrs('进行中', 3), IssueAttrs('已解决', 4), IssueAttrs('已关闭', 5), IssueAttrs('已拒绝', 6)] 16 | PRIORITIES = [IssueAttrs('所有', 0), IssueAttrs('紧急', 1), IssueAttrs('高', 2), IssueAttrs('普通', 3), IssueAttrs('低', 4)] 17 | 18 | TRACKERS_VAL = [1, 2, 3] 19 | STATUSES_VAL = [1, 2, 3, 4, 5, 6] 20 | PRIORITIES_VAL = [1, 2, 3, 4] 21 | 22 | REV_TRACKERS = {1: u'缺陷', 2: u'功能', 3: u'支持'} 23 | REV_STATUSES = {1: u'新建', 2: u'已指派', 3: u'进行中', 4: u'已解决', 5: u'已关闭', 6: u'已拒绝'} 24 | REV_PRIORITIES = {1: u'紧急', 2: u'高', 3: u'普通', 4: u'低'} 25 | 26 | ISSUE_ATTRS = { u'TRACKERS': TRACKERS, u'STATUSES': STATUSES, u'PRIORITIES': PRIORITIES, 'REV_TRACKERS': REV_TRACKERS, 'REV_STATUSES': REV_STATUSES, 'REV_PRIORITIES': REV_PRIORITIES, } 27 | -------------------------------------------------------------------------------- /issue/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /keyauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/keyauth/__init__.py -------------------------------------------------------------------------------- /keyauth/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from gitshell.objectscache.models import BaseModel 3 | from gitshell.objectscache.da import query, queryraw, execute, count, countraw 4 | 5 | class UserPubkey(BaseModel): 6 | user_id = models.IntegerField(null=False) 7 | name = models.CharField(max_length=32, null=False) 8 | key = models.CharField(max_length=1024, null=False) 9 | fingerprint = models.CharField(max_length=64, db_index=True, null=False) 10 | 11 | class KeyauthManager(): 12 | 13 | @classmethod 14 | def list_userpubkey_by_userId(self, user_id): 15 | userPubkeys = query(UserPubkey, user_id, 'userpubkey_l_userId', [user_id]) 16 | return userPubkeys 17 | 18 | @classmethod 19 | def list_userpubkey_by_fingerprint(self, fingerprint): 20 | userPubkeys = queryraw(UserPubkey, 'userpubkey_l_fingerprint', [fingerprint]) 21 | return userPubkeys 22 | 23 | @classmethod 24 | def get_userpubkey_by_id(self, user_id, pid): 25 | userPubkeys = query(UserPubkey, user_id, 'userpubkey_s_id', [user_id, pid]) 26 | if len(userPubkeys) > 0: 27 | return userPubkeys[0] 28 | return None 29 | 30 | @classmethod 31 | def get_userpubkey_by_userId_fingerprint(self, user_id, fingerprint): 32 | userPubkeys = query(UserPubkey, user_id, 'userpubkey_s_userId_fingerprint', [user_id, fingerprint]) 33 | if len(userPubkeys) > 0: 34 | return userPubkeys[0] 35 | return None 36 | 37 | @classmethod 38 | def count_userpubkey_by_fingerprint(self, fingerprint): 39 | return countraw('userpubkey_c_fingerprint', [fingerprint]) 40 | 41 | @classmethod 42 | def get_userpubkey_by_fingerprint(self, fingerprint): 43 | userPubkeys = queryraw(UserPubkey, 'userpubkey_s_fingerprint', [fingerprint]) 44 | if len(userPubkeys) > 0: 45 | return userPubkeys[0] 46 | return None 47 | 48 | -------------------------------------------------------------------------------- /keyauth/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /keyvalue/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/keyvalue/__init__.py -------------------------------------------------------------------------------- /keyvalue/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from gitshell.objectscache.models import BaseModel 3 | 4 | # common key value 5 | class Keyvalue(BaseModel): 6 | key_type = models.IntegerField(null=False) 7 | user_id = models.IntegerField(null=False) 8 | key_name = models.CharField(max_length=16, null=False) 9 | key_value = models.CharField(max_length=2048, null=False) 10 | 11 | # note 12 | # create index for (key_type, user_id, key_name) 13 | -------------------------------------------------------------------------------- /keyvalue/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /keyvalue/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gitshell.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /objectscache/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/objectscache/__init__.py -------------------------------------------------------------------------------- /objectscache/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class BaseModel(models.Model): 4 | create_time = models.DateTimeField(auto_now=False, auto_now_add=True, null=False) 5 | modify_time = models.DateTimeField(auto_now=True, auto_now_add=True, null=False) 6 | visibly = models.SmallIntegerField(default=0, null=False) 7 | 8 | class Meta: 9 | abstract = True 10 | 11 | class Count(models.Model): 12 | count = models.IntegerField(default=0, null=False) 13 | 14 | class Select(models.Model): 15 | pass 16 | 17 | class CacheKey: 18 | REPO_COMMIT_VERSION = 'repo.commit_version_%s' 19 | REPO_META = 'repo.meta_%s_%s' 20 | JOIN_CLIENT_IP = 'join.client_ip.%s' 21 | -------------------------------------------------------------------------------- /objectscache/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /objectscache/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /render_settins.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/python 3 | import os, sys 4 | import ConfigParser 5 | from string import Template 6 | import time 7 | if __name__ == '__main__': 8 | os.chdir(os.path.dirname(os.path.abspath(__file__))) 9 | if not os.path.exists('env.ini'): 10 | print 'can not found env.ini, exit now' 11 | sys.exit(128) 12 | config = ConfigParser.RawConfigParser() 13 | with open('env.ini', 'r') as envF, open('settings.tmpl', 'r') as tmplF, open('settings.py', 'w') as settingsF: 14 | config.readfp(envF) 15 | items = dict(config.items('env')) 16 | items['timestamp'] = str(time.time()) 17 | properties_tmpl = tmplF.read() 18 | template = Template(properties_tmpl) 19 | settingsF.write(template.substitute(items)) 20 | 21 | ########### env.ini ########### 22 | """ 23 | [env] 24 | debug = True 25 | template_debug = True 26 | user = git 27 | password = gitshell 28 | host = 127.0.0.1 29 | template_dirs = /opt/app/8001/gitshell/templates 30 | logging_file_path = /opt/run/var/log/gitshell.8001.log 31 | secret_key = '' 32 | github_client_id = '' 33 | github_client_secret = '' 34 | dropbox_app_key = '' 35 | dropbox_app_secret = '' 36 | dropbox_access_token = '' 37 | dropbox_access_token_secret = '' 38 | """ 39 | -------------------------------------------------------------------------------- /repo/Forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django import forms 3 | from django.forms.widgets import RadioSelect, CheckboxSelectMultiple 4 | from gitshell.repo.models import Repo, RepoManager 5 | from gitshell.feed.feed import AttrKey, FeedAction 6 | from gitshell.gsuser.models import GsuserManager 7 | from gitshell.team.models import TeamManager 8 | 9 | LANG_CHOICES = (('C', 'C'), ('Java', 'Java'), ('C++', 'C++'), ('Objective-C', 'Objective-C'), ('PHP', 'PHP'), ('Python', 'Python'), ('JavaScript', 'JavaScript'), ('Ruby', 'Ruby'), ('C#', 'C#'), ('Erlang', 'Erlang'), ('Bash', 'Bash'), ('Awk', 'Awk'), ('Scala', 'Scala'), ('Go', 'Go'), ('Haskell', 'Haskell'), ('Perl', 'Perl'), ('Lisp', 'Lisp'), ('PL/SQL', 'PL/SQL'), ('Lua', 'Lua'), ('(Visual)', '(Visual)'), ('Basic', 'Basic'), ('MATLAB', 'MATLAB'), ('Delphi/Object', 'Delphi/Object'), ('Pascal', 'Pascal'), ('Visual', 'Visual'), ('Basic', 'Basic'), ('.NET', '.NET'), ('Pascal', 'Pascal'), ('Ada', 'Ada'), ('Transact-SQL', 'Transact-SQL'), ('Logo', 'Logo'), ('NXT-G', 'NXT-G'), ('SAS', 'SAS'), ('Assembly', 'Assembly'), ('ActionScript', 'ActionScript'), ('Fortran', 'Fortran'), ('RPG', 'RPG'), ('(OS/400)', '(OS/400)'), ('Scheme', 'Scheme'), ('COBOL', 'COBOL'), ('Groovy', 'Groovy'), ('R', 'R'), ('ABAP', 'ABAP'), ('cg', 'cg'), ('Scratch', 'Scratch'), ('D', 'D'), ('Prolog', 'Prolog'), ('F#', 'F#'), ('APL', 'APL'), ('Smalltalk', 'Smalltalk'), ('(Visual)', '(Visual)'), ('FoxPro', 'FoxPro'), ('Forth', 'Forth'), ('ML', 'ML'), ('Alice', 'Alice'), ('CFML', 'CFML'), ('VBScript', 'VBScript'), ('other', '其他')) 10 | AUTH_TYPE_CHOICES = (('2', '私有'), ('0', '公开'), ('1', '半公开')) 11 | 12 | class RepoForm(forms.ModelForm): 13 | 14 | def fill_username(self, userprofile, owner_user_id): 15 | feedAction = FeedAction() 16 | teamMembers = TeamManager.list_teamMember_by_userId(userprofile.id) 17 | username_choices = [] 18 | username_choices.append((userprofile.username, userprofile.username)) 19 | for teamMember in teamMembers: 20 | if teamMember.team_user_id == owner_user_id: 21 | username_choices.insert(0, (teamMember.team_user.username, teamMember.team_user.username)) 22 | continue 23 | username_choices.append((teamMember.team_user.username, teamMember.team_user.username)) 24 | self.fields['username'] = forms.ChoiceField(choices=username_choices, required=False) 25 | 26 | class Meta: 27 | model = Repo 28 | fields = ('username', 'name', 'desc', 'lang', 'auth_type',) 29 | widgets = { 30 | 'desc': forms.Textarea(attrs={'cols': 60, 'rows': 5, 'maxlength': 512}), 31 | 'lang': forms.Select(choices=LANG_CHOICES), 32 | 'auth_type': forms.RadioSelect(choices=AUTH_TYPE_CHOICES), 33 | } 34 | 35 | -------------------------------------------------------------------------------- /repo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/repo/__init__.py -------------------------------------------------------------------------------- /repo/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /skills/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/skills/__init__.py -------------------------------------------------------------------------------- /skills/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /skills/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /skills/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.http import HttpResponse, HttpResponseRedirect, Http404 3 | from django.template import RequestContext 4 | from django.shortcuts import render_to_response 5 | from django.contrib.auth.models import User 6 | from django.contrib.auth.decorators import login_required 7 | from gitshell.gsuser.Forms import SkillsForm 8 | 9 | def skills(request): 10 | skillsForm = SkillsForm() 11 | response_dictionary = {'ii': range(0, 5), 'jj': range(0, 3), 'kk': range(0, 10), 'skillsForm': skillsForm} 12 | return render_to_response('skills/skills.html', 13 | response_dictionary, 14 | context_instance=RequestContext(request)) 15 | -------------------------------------------------------------------------------- /stats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/stats/__init__.py -------------------------------------------------------------------------------- /stats/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /stats/timeutils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json, time 3 | from dateutil.relativedelta import relativedelta 4 | from datetime import datetime 5 | from datetime import timedelta 6 | 7 | # NOTE: not multi thread safe 8 | round_hours_dict = {} 9 | round_days_dict = {} 10 | round_months_dict = {} 11 | 12 | def getlast12hours(now): 13 | global round_hours_dict 14 | if now is None: 15 | now = datetime.now() 16 | now_hour = datetime(now.year, now.month, now.day, now.hour) 17 | mktime_now_hour = time.mktime(now_hour.timetuple()) 18 | if mktime_now_hour in round_hours_dict: 19 | return round_hours_dict[mktime_now_hour] 20 | if len(round_hours_dict) > 100: 21 | round_hours_dict = {} 22 | round_hours = [] 23 | i = 0 24 | while i < 12: 25 | delta_hour = now_hour + timedelta(hours=-i) 26 | i = i + 1 27 | round_hours.append(time.mktime(delta_hour.timetuple())) 28 | round_hours_dict[mktime_now_hour] = round_hours 29 | return round_hours 30 | 31 | def getlast7days(now): 32 | return getlast30days(now)[0:7] 33 | 34 | def getlast30days(now): 35 | global round_days_dict 36 | if now is None: 37 | now = datetime.now() 38 | now_day = datetime(now.year, now.month, now.day) 39 | mktime_now_day = time.mktime(now_day.timetuple()) 40 | if mktime_now_day in round_days_dict: 41 | return round_days_dict[mktime_now_day] 42 | if len(round_days_dict) > 100: 43 | round_days_dict = {} 44 | fullfill_days_dict(now_day) 45 | return round_days_dict[mktime_now_day] 46 | 47 | def getlast12months(now): 48 | global round_months_dict 49 | if now is None: 50 | now = datetime.now() 51 | now_month = datetime(now.year, now.month, 1) 52 | mktime_now_month = time.mktime(now_month.timetuple()) 53 | if mktime_now_month in round_months_dict: 54 | return round_months_dict[mktime_now_month] 55 | if len(round_months_dict) > 100: 56 | round_months_dict = {} 57 | round_months = [] 58 | i = 0 59 | while i < 12: 60 | delta_month = now_month + relativedelta(months=-i) 61 | i = i + 1 62 | round_months.append(time.mktime(delta_month.timetuple())) 63 | round_months_dict[mktime_now_month] = round_months 64 | return round_months 65 | 66 | def get_round_day(now): 67 | round_day = datetime(now.year, now.month, now.day) 68 | return round_day 69 | 70 | def get_round_week(now): 71 | round_day = datetime(now.year, now.month, now.day) 72 | round_week = round_day + timedelta(days=-now.weekday()) 73 | return round_week 74 | 75 | def get_round_month(now): 76 | round_month = datetime(now.year, now.month, 1) 77 | return round_month 78 | 79 | def get_round_year(now): 80 | round_year = datetime(now.year, 1, 1) 81 | return round_year 82 | 83 | def fullfill_days_dict(now_day): 84 | global round_days_dict 85 | round_days = [] 86 | i = 0 87 | while i < 30: 88 | delta_day = now_day + timedelta(days=-i) 89 | i = i + 1 90 | round_days.append(time.mktime(delta_day.timetuple())) 91 | mktime_now_day = time.mktime(now_day.timetuple()) 92 | round_days_dict[mktime_now_day] = round_days 93 | 94 | -------------------------------------------------------------------------------- /stats/views.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/stats/views.py -------------------------------------------------------------------------------- /team/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/team/__init__.py -------------------------------------------------------------------------------- /team/decorators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.http import Http404 3 | from django.http import HttpResponseRedirect 4 | from django.utils.http import urlquote 5 | from gitshell.gsuser.models import GsuserManager 6 | from gitshell.help.views import error_with_reason 7 | from gitshell.team.models import TeamManager, TeamMember 8 | from gitshell.viewtools.views import json_httpResponse, json_success, json_failed, obj2dict 9 | 10 | def team_admin_permission_check(function): 11 | 12 | def wrap(request, *args, **kwargs): 13 | if len(args) >= 1: 14 | username = args[0] 15 | teamUser = GsuserManager.get_user_by_name(username) 16 | if not teamUser: 17 | return _response_not_admin_rights(request) 18 | if not request.user.is_authenticated(): 19 | return HttpResponseRedirect('/login/?next=' + urlquote(request.path)) 20 | teamMember = TeamManager.get_teamMember_by_teamUserId_userId(teamUser.id, request.user.id) 21 | if not teamMember or not teamMember.has_admin_rights(): 22 | return _response_not_admin_rights(request) 23 | return function(request, *args, **kwargs) 24 | wrap.__doc__=function.__doc__ 25 | wrap.__name__=function.__name__ 26 | 27 | return wrap 28 | 29 | def _response_not_admin_rights(request): 30 | if request.method == 'POST': 31 | return json_failed(403, u'没有管理员权限') 32 | return error_with_reason(request, 'repo_permission_denied') 33 | 34 | -------------------------------------------------------------------------------- /team/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /templates/email.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Gitshell Email模板 6 | 7 | 8 | 9 | 10 | 11 | 33 | 34 | 35 |
12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 28 | 29 | 30 |
17 |

你好cloudzhou,

18 |
22 |

请点击左边的按钮验证你的邮箱。验证邮箱

23 | 24 |
25 |

此邮件由Gitshell系统发出,不接收回信,因此请勿直接回复。 如需要任何帮助,请联系 support@gitshell.com

26 |
27 |
31 | 32 |
36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 65 | 66 | 67 |
44 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 60 | 61 | 62 |
49 |

cloudzhou评论了问题:

50 |
54 |

cloudzhou说:@cloudzhou xyz cloudzhou/gitshell # Sept. 6, 2013, 12:22 p.m.

55 | 56 |
57 |

此邮件由Gitshell系统发出,不接收回信,因此请勿直接回复。 如需要任何帮助,请联系 support@gitshell.com

58 |
59 |
63 | 64 |
68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /templates/help/access_out_of_limit.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block css %} 3 | 6 | {% endblock %} 7 | {% block container %} 8 | 11 |
12 |
很不幸让你看到这里,这个页面说明您在 X 时间段之内访问次数超出了限制的 N 次
13 | 以下几种可能导致了这样的结果:
14 |   1) 今天您的访问次数实在太多了,建议休息一下吧
15 |   2) 您不断的在刷新页面,比如您的猫一直在按 F5 键
16 |   3) 脚本,爬虫的行为,哦,请不要这么做!
17 | 也有可能是我们的错,如果这个页面频繁出现,请联系 support@gitshell.com
18 | 要解决这个问题,您可以发送以下万恶的验证码:
19 |         
20 |
21 | {% csrf_token %} 22 | {{ resetAccessLimitForm.captcha }} 23 | 24 |
25 |
26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /templates/help/error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block container_class_name %}error{% endblock %} 4 | {% block container %} 5 |
6 |
7 |
8 |
9 |

404,对不起,您要找的页面不存在。

10 |

返回首页

11 |
12 |
13 |
14 |
15 | {% endblock %} 16 | 17 | {% block js %} 18 | 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /templates/help/error_with_reason.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block container_class_name %}error{% endblock %} 4 | {% block container %} 5 |
6 |
7 |
8 |
9 |

10 | {%if reason == 'repo_permission_denied'%} 11 | 该仓库为私有,没有权限查看 12 | {%elif reason == 'repo_not_found'%} 13 | 没有该仓库,请确认输入正确 14 | {%elif reason == 'team_not_admin_rights'%} 15 | 没有该团队账户的管理权限 16 | {%endif%} 17 |

18 |

返回首页

19 |
20 |
21 |
22 |
23 | {% endblock %} 24 | 25 | {% block js %} 26 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load gstools %} 4 | {% block container_class_name %}welcome{% endblock %} 5 | {% block container %} 6 |
7 |
8 | 9 | 14 | 28 | 29 |
30 |
31 | 32 | {% endblock %} 33 | 34 | {% block js %}{% endblock %} 35 | -------------------------------------------------------------------------------- /templates/repo/advance.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/settings_base.html" %} 2 | {% block settingcontainer %} 3 |
4 |

Deploy URL

5 |
6 | {% if repo.deploy_url and repo.deploy_url != '' %} 7 |

8 | 9 | 10 |

11 |
https://{{repo.deploy_url}}@gitshell.com/{{user_name}}/{{repo_name}}.git
12 | {% else %} 13 |

14 |

deploy url 是 git 只读、不需要权限验证的 clone 地址,用于发布、部署,不能公开该地址

15 | {% endif %} 16 |
17 |
18 |
19 |

Dropbox 及时备份

20 |
21 | {% if repo.dropbox_sync == 1 %} 22 |

23 |
24 | {% if repo.dropbox_url == '' %} 25 | Dropbox 正在同步中... 请三分钟之后再来围观 26 | {% else %} 27 | {{ repo.dropbox_url }} 28 | {% endif %} 29 |
30 | {% else %} 31 |

32 |

仓库将近乎及时地备份在 Dropbox,加入本地 Dropbox 后可以直接 git clone,不能公开该地址

33 | {% endif %} 34 |
35 |
36 | {% endblock %} 37 | {% block subjs %} 38 | 66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /templates/repo/blob.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | {% block css %} 3 | 4 | {% endblock %} 5 | 6 | {% block subcontainer %} 7 |
8 |
9 |
10 |
11 | {% include "repo/branch_nav.html" %} 12 |
13 | 14 | {{repo_name}}/ 15 | 16 | 17 | 18 | 提交 19 | 原始文件 20 | 21 |
22 |
23 | 24 |
25 | {% if not blob and blob != '' %} 26 |

没有源代码、二进制文件,或者没有查看源代码权限,半公开和私有项目需要申请成为成员才能查看源代码

27 | {% else %} 28 | {% if is_markdown %} 29 |
{{ blob }}
30 | {% else %} 31 | {{ blob|safe }} 32 | {% endif %} 33 | {% endif %} 34 |
35 |
36 | 37 |
38 | {% endblock %} 39 | {% block subjs %} 40 | {% if is_markdown %} 41 | 42 | 43 | {% endif %} 44 | 87 | {% endblock %} 88 | -------------------------------------------------------------------------------- /templates/repo/branch_nav.html: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /templates/repo/branches.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 | {% endblock %} 6 | 7 | {% block subjs %} 8 | 65 | {% endblock %} 66 | -------------------------------------------------------------------------------- /templates/repo/branches_permission.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 | 8 |
9 |

分支权限控制

10 |

常见使用说明

11 |
12 | 13 |

分支权限控制

14 |
15 | 22 |
23 | 24 |
25 |
26 | 1 xx
27 | 2 yy
28 |     
29 |
30 |
31 | {% endblock %} 32 | 33 | {% block subjs %} 34 | 35 | 39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /templates/repo/commit.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | 3 | {% block subcontainer %} 4 | 5 |
6 | {% if commit %} 7 |
8 |
9 |
{{commit.committer_name}}
10 |
11 |

{{commit.commit_message|truncatechars:80}}

12 |

13 | {% if commit.real_committer_name %} 14 | {{commit.committer_name}} 15 | {% else %} 16 | {{commit.committer_name}} 17 | {% endif %} 18 | {{commit.committer_date}} 19 |

20 |
21 |
22 |
23 | 父对象 24 | {% for parent_commit_hash in commit.parent_commit_hash %} 25 | {{parent_commit_hash}} 26 | {% endfor %} 27 |
28 |
29 | 34 | {% else %} 35 |
36 |

没有找到该 Commit

37 |
38 | {% endif %} 39 |
40 |
$ git diff ...{{commit_hash}}
41 | {% for parent_commit_hash in commit.parent_commit_hash %} 42 |
43 | {% endfor %} 44 |
45 | 46 |
47 | {% endblock %} 48 | 49 | {% block subjs %} 50 | 76 | {% endblock %} 77 | -------------------------------------------------------------------------------- /templates/repo/compare.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | 3 | {% block container_class_name %}repo{% endblock %} 4 | 5 | {% block subcontainer %} 6 |
7 |
8 | {% if is_repo_member %} 9 | 13 | {% endif %} 14 |
15 | 16 | {# compare #} 17 |

比较

18 |
19 |
20 | 28 |
29 |
30 |
31 | 39 |
40 |
41 | 42 |
43 | 44 |
45 |

 46 |     确定
 47 |   
48 |
49 | {% endblock %} 50 | {% block subjs %} 51 | 98 | {% endblock %} 99 | 100 | 101 | -------------------------------------------------------------------------------- /templates/repo/condition.html: -------------------------------------------------------------------------------- 1 | 50 | {# 创建问题 #} 51 | -------------------------------------------------------------------------------- /templates/repo/delete.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/settings_base.html" %} 2 | {% block settingcontainer %} 3 |
4 |

删除仓库

5 |

你正在删除仓库 {{repo_name}}, 这是不可恢复的操作

6 |
7 |
删除仓库之前最好想到以下问题:
8 |
对应数据是否备份
是否要通知相关的合作者
相关的仓库成员组织,问题,文档都将不见
9 |
10 |
11 | {% csrf_token %} 12 | 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /templates/repo/detail.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/settings_base.html" %} 2 | {% block settingcontainer %} 3 |
4 |
5 | {% csrf_token %} 6 |
{{ repoForm.username }}
7 |
8 | 9 |
10 | {{ repoForm.name }} 11 |
12 |
13 |
14 | 15 |
16 | {{ repoForm.desc }} 17 |
18 |
19 |
20 | 21 |
22 | {{ repoForm.lang }} 23 |
24 |
25 | 31 |
32 | 33 |
34 |
35 | {% for auth_type in repoForm.auth_type %} 36 | 37 | {{auth_type}} 38 | 39 | {% endfor %} 40 | 半公开指仅仅源代码不公开 41 |
42 |
43 |
44 |
45 | × 46 | 出错了! {{ error }} 47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 | {% endblock %} 56 | {% block subjs %} 57 | 93 | {% endblock %} 94 | -------------------------------------------------------------------------------- /templates/repo/hooks.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/settings_base.html" %} 2 | {% block settingcontainer %} 3 |
4 |
5 |

服务钩子列表

6 |

什么是服务钩子?

7 |
8 | 添加服务钩子 9 |
10 | 11 | 12 |
13 |
14 |
15 | 16 |
17 | {% if webHookURLs %} 18 | 33 | {% else %} 34 |

没有服务钩子

35 | {% endif %} 36 |
37 | 38 |
39 | {% endblock %} 40 | 41 | {% block subjs %} 42 | 91 | {% endblock %} 92 | -------------------------------------------------------------------------------- /templates/repo/issue_create.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | {% block subcontainer %} 3 |
4 |

创建问题

5 | {% if error %} 6 |
7 | 8 |

出错了!{{ error }}

9 |
10 | {% endif %} 11 |
12 | {% csrf_token %} 13 |
14 | 分配给{{ issueForm.assigned }}{{ issueForm.tracker }}{{ issueForm.status }}{{ issueForm.priority }}类别{{ issueForm.category }} 15 |
16 |
{{ issueForm.subject }}
17 |
{{ issueForm.content }}
18 |
19 | 20 |
21 |
22 |
23 | {% endblock %} 24 | {% block subjs %} 25 | 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /templates/repo/issue_edit.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | {% block subcontainer %} 3 |
4 |

修改问题

5 | {% if error %} 6 |
7 | 8 |

出错了!{{ error }}

9 |
10 | {% endif %} 11 |
12 | {% csrf_token %} 13 |
分配给{{ issueForm.assigned }}{{ issueForm.tracker }}{{ issueForm.status }}{{ issueForm.priority }}类别{{ issueForm.category }}
14 |
{{ issueForm.subject }}
15 |
{{ issueForm.content }}
16 |
17 | 18 | 19 |
20 |
21 |
22 | {% endblock %} 23 | {% block subjs %} 24 | 43 | {% endblock %} 44 | 45 | 46 | -------------------------------------------------------------------------------- /templates/repo/members.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/settings_base.html" %} 2 | {% block settingcontainer %} 3 |
4 |
5 |

成员列表

6 |

所有成员默认对仓库有读写权限

7 |
8 | 添加新成员 9 | 10 |
11 | 12 | 13 |
14 |
15 |
16 | 17 |
18 | {%for member_vo in members_vo%} 19 |
20 |
21 | {{ gsuser.username }} 22 |
23 |
24 |

25 | {{member_vo.username}} 26 | {%if member_vo.nickname%} 27 | ({{member_vo.nickname}}) 28 | {%endif%} 29 | {%if member_vo.tweet%} 30 | {{member_vo.tweet}} 31 | {%endif%} 32 |

33 |

34 | {{member_vo.date_joined}}加入 35 | / 36 | {{member_vo.last_login}}登陆过 37 |

38 |
39 | {% if member_vo.username != user_name %} 40 | 41 | {% endif %} 42 |
43 | {%endfor%} 44 |
45 | 46 |
47 | {% endblock %} 48 | 49 | {% block subjs %} 50 | 51 | 87 | {% endblock %} 88 | -------------------------------------------------------------------------------- /templates/repo/network.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | 3 | {% block bubble_inner_class_name %} network{% endblock %} 4 | 5 | {% block subcontainer %} 6 | 7 |
8 |

仓库成员列表

9 |
10 | 11 |
12 | {%for member_vo in members_vo%} 13 |
14 |
15 |
16 | {{ gsuser.username }} 17 |
18 |
19 |

20 | {{member_vo.username}} 21 | {%if member_vo.nickname%} 22 | ({{member_vo.nickname}}) 23 | {%endif%} 24 |

25 |

26 | {%if member_vo.tweet%} 27 | {{member_vo.tweet}} 28 | {%endif%} 29 |

30 |
31 | {% if member_vo.username != user_name %} 32 | 33 | {% endif %} 34 |
35 |
36 | {%endfor%} 37 |
38 | 添加新成员 39 |
40 | {% csrf_token %} 41 |
42 | {{repoMemberForm.username}} 43 | {{repoMemberForm.action}} 44 | 45 |
46 |
47 |
48 |
49 | 50 | {% endblock %} 51 | 52 | {% block subjs %} 53 | 54 | 116 | {% endblock %} 117 | -------------------------------------------------------------------------------- /templates/repo/pulls.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | {% block subcontainer %} 3 |
4 |
5 |

合并请求

6 |
7 | 8 | {% if pullRequests %} 9 | {% for pullRequest in pullRequests %} 10 |
11 |
12 | {{pullRequest.status_view}} 13 | 14 | {{pullRequest.desc_refname}} 15 | 16 | {{pullRequest.source_refname}} 17 | 18 |
19 |
20 |

21 | {{pullRequest.short_title}} 22 | #{{pullRequest.id}} 23 |

24 |
25 | {% if pullRequest.short_desc %} 26 |

{{pullRequest.short_desc}}

27 | {% else %} 28 |

没有介绍内容

29 | {% endif %} 30 |
31 |
32 | {{pullRequest.pull_user.username}} 33 | 34 |
35 |
36 |
37 | {% endfor %} 38 | {% else %} 39 |

没有合并请求。创建合并请求

40 | {% endif %} 41 |
42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /templates/repo/refs_create.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | {% block subcontainer %} 3 |
4 | 5 |

创建分支

6 | 7 |
8 |
9 |
10 | 11 | 16 | 17 | 21 | 22 | 确定 23 |
24 |
25 |
26 | 27 |
28 | {% endblock %} 29 | {% block subjs %} 30 | 78 | {% endblock %} 79 | -------------------------------------------------------------------------------- /templates/repo/settings_base.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | {% block subcontainer %} 3 |
4 |

仓库设置

5 | 15 |
16 | {% block settingcontainer %}{% endblock %} 17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /templates/repo/shared/base_fields.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
{{ repoForm.name }}该仓库名已经存在或者不符合规范
4 |
5 | 6 |
7 | 8 |
{{ repoForm.desc }}
9 |
10 | 11 |
12 | 13 |
{{ repoForm.lang }}
14 |
15 | 16 |
17 | 18 |
19 |
20 | 21 |
22 | 23 |
24 | {% for radio in repoForm.auth_type %} 25 | {{ radio }} 26 | {% endfor %} 27 |

公开:包括源代码公开; 半公开:除了源代码之外公开; 私有:对外不公开

28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | -------------------------------------------------------------------------------- /templates/repo/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "repo/repo.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 | {% endblock %} 6 | 7 | {% block subjs %} 8 | 60 | {% endblock %} 61 | -------------------------------------------------------------------------------- /templates/repo/url.html: -------------------------------------------------------------------------------- 1 |
2 | {% if repo.status != 0 %} 3 |

仓库正在初始化中...暂时不能提交、查看源代码,请耐心等候,稍后刷新。(这种情况很可能是初始化一个较大的仓库)

4 | {% endif %} 5 | {% if has_fork_right %} 6 |
7 |
8 | {% if is_repo_member %} 9 | SSH 10 | {% endif %} 11 | HTTP 12 | {% if repo.auth_type == 0 %} 13 | GIT 14 | {% endif %} 15 |
16 |
17 | {% if is_repo_member %} 18 | 19 | git@gitshell.com:{{ user_name }}/{{ repo_name }}.git 20 | 21 | {% elif repo.auth_type == 0 %} 22 | 23 | https://gitshell.com/{{ user_name }}/{{ repo_name }}.git 24 | 25 | {% endif %} 26 | 27 | 复制 28 | 29 | {% if is_repo_member %}读写{% elif repo.auth_type == 0 %}只读{% endif %} 30 |
31 |
32 | {% endif %} 33 |
34 | -------------------------------------------------------------------------------- /templates/repo/user_repo.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body_class_name %}user{% endblock %} 4 | {% block container_class_name %}dashboard{% endblock %} 5 | 6 | {% block container %} 7 | 8 |
9 |
10 |

我的仓库({{userprofile.get_total_repo}})

11 |
12 |
13 | 14 |
15 |
16 |
17 | 18 | {% load humanize %} 19 | 20 | {% if repo_list %} 21 | {% for repo in repo_list %} 22 |
23 | 24 | 29 | 30 |
31 | {{repo.name}} 32 | {% if repo.is_public %} 33 | 34 | {% else %} 35 | 36 | {% endif %} 37 |
38 |
39 |
40 |

{{repo.name}}

41 |

Fork自yuwen/gitshell

42 |
43 | 44 |
45 |

{{repo.desc|truncatechars:"80"}}

46 |
47 |
48 | 49 |
50 | {% endfor %} 51 | {% else %} 52 |

你还没没有创建任何仓库。

53 | {% endif %} 54 | 55 | {% comment %} 56 | 92 | {% endcomment %} 93 | 94 |
95 |
96 |
97 | 98 | {% endblock %} 99 | 100 | {% block js %} 101 | 106 | {% endblock %} 107 | -------------------------------------------------------------------------------- /templates/settings/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block container_class_name %}settings{% endblock %} 4 | 5 | {% block container %} 6 | 7 |
8 |
9 |

设置

10 |
11 |
12 | 13 |
14 |
15 | 16 | 48 | 49 |
50 |
51 | {% block container-right %}{% endblock %} 52 |
53 |
54 | 55 |
56 |
57 | 58 | {% endblock %} 59 | 60 | -------------------------------------------------------------------------------- /templates/settings/changepassword.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | {% block container-right %} 3 |
4 | 修改密码 5 | {% csrf_token %} 6 |
7 | 8 |
{{ changepasswordForm.password }}
9 |
10 | {% if error %} 11 |
12 | × 13 | 出错了! {{ error }} 14 |
15 | {% endif %} 16 |
17 |
18 | 19 |
20 |
21 |
22 | {% endblock %} 23 | 24 | {% block js %} 25 | 37 | {% endblock %} 38 | 39 | -------------------------------------------------------------------------------- /templates/settings/default.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | {% block container-right %} 3 |

愿景

4 |

aa

5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /templates/settings/destroy.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | {% block container-right %} 3 |

关于删除帐号

4 |
5 |

gitshell 是一个纯粹的开发者社区,没有太多的私人社交信息,事实上,我们非常谨慎的添加社交元素,为了保证社区的纯洁和安静。假如您真的不想再待在Gitshell,可以按照以下的步骤删除相应的内容:

6 |
    7 |
  1. 删除您的所有仓库
  2. 8 |
  3. 删除您所有的SSH公钥
  4. 9 |
  5. 清空所有的个人信息,如有必填项建议可以填写"Goodbye,Gitshell"
  6. 10 |
  7. 修改您的密码,当然您可以做的疯狂一些,不要犹豫q)2[]ond,xasl;m,l;xasn2aid都是允许的,尽情敲击您的键盘吧。
  8. 11 |
12 |

为了保证引用的完整性,gitshell不会直接删除帐号,假如您觉得这一切还不够,请联系 support@gitshell.com

13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /templates/settings/emails.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | 3 | {% block container-right %} 4 |
5 |

管理邮箱

6 | 31 |
32 | 33 |
34 |

添加新的邮箱

35 |
36 | 37 | 38 |
39 |
40 | {% endblock %} 41 | 42 | {% block js %} 43 | 78 | {% endblock %} 79 | -------------------------------------------------------------------------------- /templates/settings/notif.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | {% block container-right %} 3 |

消息设置

4 |

设置接收哪些消息以邮件的方式通知你

5 |
6 |
@你的
7 |
8 | {% for x in notif_type_choice.at %} 9 | 10 | 11 | 12 | 13 | {% endfor %} 14 |
15 |
合并请求
16 |
17 | {% for x in notif_type_choice.merge %} 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 |
24 |
问题
25 |
26 | {% for x in notif_type_choice.issue %} 27 | 28 | 29 | 30 | 31 | {% endfor %} 32 |
33 |
发送频率
34 |
35 | 36 | 41 | 42 |
43 |
接收邮箱
44 |
45 | 57 |
58 |
59 |

60 | {% endblock %} 61 | {% block js %} 62 | 100 | {% endblock %} 101 | -------------------------------------------------------------------------------- /templates/settings/sshpubkey.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | 3 | {% block container-right %} 4 | {% if userPubkey_all %} 5 |

管理SSH公匙

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for userPubkey in userPubkey_all %} 23 | 24 | 25 | 26 | 27 | 30 | 31 | {% endfor %} 32 | 33 |
公钥标识指纹信息状态操作
{{ userPubkey.name }}{{ userPubkey.fingerprint }}正常 28 | 29 |
34 | {% endif %} 35 | 36 |
37 | {% csrf_token %} 38 | {{ doSshpubkeyForm.pubkey_id }} 39 | {{ doSshpubkeyForm.action }} 40 |
41 | 42 |
43 |
44 | 添加SSH公匙 45 | {% csrf_token %} 46 |
47 | 48 |
49 | {{ sshpubkeyForm.pubkey_name }} 50 |
51 |
52 |
53 | 54 |
55 | {{ sshpubkeyForm.pubkey }} 56 |
57 |
58 | {% if error %} 59 |
60 | × 61 | 出错了! {{ error }} 62 |
63 | {% endif %} 64 |
65 |
66 | 67 |

如何添加SSH公匙?

68 |
69 |
70 |
71 |
72 | {% endblock %} 73 | 74 | {% block js %} 75 | 99 | {% endblock %} 100 | 101 | -------------------------------------------------------------------------------- /templates/settings/team.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | 3 | {% block container-right %} 4 |

我的团队

5 | 6 | 7 | {% for teamMember in teamMembers %} 8 | 9 | 15 | 21 | 24 | 25 | {% endfor %} 26 | 27 |
10 | {{teamMember.team_user.username}}{{ teamMember.team_user.username }} 11 | {% if teamMember.has_admin_rights %} 12 | 管理员 13 | {% endif %} 14 | 16 | 20 | 22 | 23 |
28 |

创建新的团队

29 | 30 | {% endblock %} 31 | 32 | {% block js %} 33 | 60 | {% endblock %} 61 | 62 | -------------------------------------------------------------------------------- /templates/settings/team_create.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | 3 | {% block container-right %} 4 |

创建团队

5 |
6 |

用户名只能包含数字和英文

7 |

用户名已经存在

8 |
9 | {% csrf_token %} 10 |
11 | 12 |
13 | {{ teamprofileForm.username }} 14 |
15 |
16 |
17 | 18 |
19 | {{ teamprofileForm.tweet }} 20 |
21 |
22 |
23 | 24 |
25 | {{ teamprofileForm.nickname }} 26 |
27 |
28 |
29 | 30 |
31 | {{ teamprofileForm.website }} 32 |
33 |
34 |
35 | 36 |
37 | {{ teamprofileForm.company }} 38 |
39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 | {% endblock %} 48 | 49 | {% block js %} 50 | 70 | {% endblock %} 71 | 72 | -------------------------------------------------------------------------------- /templates/settings/thirdparty.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | 3 | {% block container-right %} 4 |

GitHub关联账户

5 | {% if thirdpartyUser %} 6 | 12 | {% else %} 13 | 关联 GitHub 账户 14 | {% endif %} 15 | {% endblock %} 16 | 17 | {% block js %} 18 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/settings/validate_email.html: -------------------------------------------------------------------------------- 1 | {% extends "settings/base.html" %} 2 | {% block container-right %} 3 | {% if validate_result == 'success' %} 4 |
5 |

修改成功,根据您目前用户名和登录邮箱,请更新仓库 url,修改 git 相关属性:

6 |

> git config --global user.name "{{user.username}}"

7 |

> git config --global user.email "{{user.email}}"

8 |
9 | {% elif validate_result == 'token_expired' %} 10 |
11 | 更改邮箱失败,可能是URL Token过期,请重新通过 修改用户名·登录邮箱 进行更改, 12 |
13 | 如果这种情况持续出现,请联系 support@gitshell.com 14 |
15 | {% elif validate_result == 'user_exists' %} 16 |
17 | 您要更改的邮箱已经存在了,请重新通过 修改用户名·登录邮箱 进行更改 18 |
19 | {% endif %} 20 | {% endblock %} 21 | {% block js %} 22 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /templates/team/dashboard_base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block container_class_name %}dashboard{% endblock %} 4 | 5 | {% block container %} 6 | 7 |
8 |
9 |
19 |
20 | 21 |
22 |
23 | {% block subcontainer %}{% endblock %} 24 |
25 |
26 | 27 | {% endblock %} 28 | 29 | {% block js %} 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/team/destroy.html: -------------------------------------------------------------------------------- 1 | {% extends "team/settings_base.html" %} 2 | {% block container-right %} 3 |

删除团队帐号

4 |
5 |

gitshell 是一个纯粹的开发者社区,没有太多的私人社交信息,事实上,我们非常谨慎的添加社交元素,为了保证社区的纯洁和安静。

6 |

删除团队操作是不可逆的行为,将导致以下后果:

7 |
    8 |
  1. 团队帐号不可用
  2. 9 |
  3. 所有的成员信息、关系不可见
  4. 10 |
  5. 所有的团队仓库不可用
  6. 11 |
  7. 其他和团队帐号有关的
  8. 12 |
13 |

尽可能的不要删除团队帐号以导致引用不完整的情况,如果你还确定这么做,请点击

14 |
15 | {% endblock %} 16 | {% block js %} 17 | 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /templates/team/group.html: -------------------------------------------------------------------------------- 1 | {% extends "team/settings_base.html" %} 2 | 3 | {% block container-right %} 4 |

{{teamGroup.name}} 组

5 |
6 | {% for groupMember in groupMembers %} 7 |
8 |
9 | 删除 10 |
11 | 15 |
16 | {% endfor %} 17 |
18 | {% if teamMembersNotInGroup %} 19 |
20 | 25 | 26 |
27 | {% endif %} 28 | {% endblock %} 29 | 30 | {% block js %} 31 | 65 | {% endblock %} 66 | 67 | -------------------------------------------------------------------------------- /templates/team/groups.html: -------------------------------------------------------------------------------- 1 | {% extends "team/settings_base.html" %} 2 | 3 | {% block container-right %} 4 |

团队组

5 | {% for teamGroup in teamGroups %} 6 |
7 |
8 | 删除 9 |
10 |
11 | {{teamGroup.name}} 12 | {{teamGroup.name}} 13 |
14 |
15 | {% endfor %} 16 |
17 | 18 | 19 |
20 | 21 | {% endblock %} 22 | 23 | {% block js %} 24 | 60 | {% endblock %} 61 | 62 | -------------------------------------------------------------------------------- /templates/team/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "team/settings_base.html" %} 2 | 3 | {% block container-right %} 4 |

创建团队

5 |
6 |

用户名不符合规范,只允许[a-zA-Z0-9_]范围的字符

7 |

用户名已经存在

8 |
9 | {% csrf_token %} 10 |
11 | 12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 | {{ teamprofileForm.tweet }} 20 |
21 |
22 |
23 | 24 |
25 | {{ teamprofileForm.nickname }} 26 |
27 |
28 |
29 | 30 |
31 | {{ teamprofileForm.website }} 32 |
33 |
34 |
35 | 36 |
37 | {{ teamprofileForm.company }} 38 |
39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 | {% endblock %} 48 | 49 | {% block js %} 50 | 70 | {% endblock %} 71 | 72 | -------------------------------------------------------------------------------- /templates/team/pull_merge.html: -------------------------------------------------------------------------------- 1 | {% extends "team/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 | 5 |
6 |
7 |

需要我处理的

8 | 9 | 需要我处理的 10 | 我创建的 11 | 12 |
13 | 14 | {% if pullRequests %} 15 | {% for pullRequest in pullRequests %} 16 |
17 |
18 | {{pullRequest.status_view}} 19 | 20 | {{pullRequest.source_refname}} 21 | 22 | {{pullRequest.desc_refname}} 23 | 24 |
25 |
26 |

27 | {{pullRequest.short_title}} 28 | #{{pullRequest.id}} 29 |

30 |
31 |

{{pullRequest.short_desc}}

32 |
33 |
34 | {{pullRequest.pull_user.username}} 35 | 36 |
37 |
38 |
39 | {% endfor %} 40 | {% else %} 41 |
42 |

没有合并请求需要处理

43 |
44 | {% endif %} 45 | 46 |
47 | 48 | 49 | {% endblock %} 50 | 51 | {% block js %} 52 | 63 | {% endblock %} 64 | -------------------------------------------------------------------------------- /templates/team/pull_request.html: -------------------------------------------------------------------------------- 1 | {% extends "team/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 | 5 |
6 |
7 |

我创建的

8 | 9 | 需要我处理的 10 | 我创建的 11 | 12 |
13 | 14 | {% if pullRequests %} 15 | {% for pullRequest in pullRequests %} 16 |
17 | 18 |
19 | 20 |
21 | {{pullRequest.pull_user.username}} 22 |
23 | 24 |
25 | 26 |

27 | {##{{pullRequest.id}}#}{{pullRequest.short_title}} 28 | {{pullRequest.status_view}} 29 |

30 | 31 |
32 | 33 | 34 | 我想把 35 | 36 | 37 | {{pullRequest.source_repo.name}}:{{pullRequest.source_refname}} 38 | 合并到 39 | {{pullRequest.desc_repo.name}}:{{pullRequest.desc_refname}} 40 | 41 | 42 |
43 | 44 |

{{pullRequest.short_desc}}

45 | 46 |
47 | 48 |
49 |
50 | {% endfor %} 51 | {% else %} 52 |
53 |

没有合并请求

54 |
55 | {% endif %} 56 |
57 | 58 | 59 | {% endblock %} 60 | 61 | {% block js %} 62 | 73 | {% endblock %} 74 | -------------------------------------------------------------------------------- /templates/team/repo.html: -------------------------------------------------------------------------------- 1 | {% extends "team/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 | 5 | {#

我的仓库({{teamUserprofile.get_total_repo}})

#} 6 | 7 |
8 | 9 | {% load humanize %} 10 | 11 | {% if repos %} 12 | {% for repo in repos %} 13 |
14 | 15 | 20 | 21 |
22 | {{repo.name}} 23 | 24 |
25 |
26 |
27 |

{{repo.name}}

28 |
29 | 30 |
31 |

{{repo.desc}}

32 |
33 |
34 | 35 |
36 | {% endfor %} 37 | {% else %} 38 |

没有仓库

39 | {% endif %} 40 | 41 |
42 | 43 | {% endblock %} 44 | 45 | {% block js %} 46 | 49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /templates/team/settings_base.html: -------------------------------------------------------------------------------- 1 | {% extends "team/dashboard_base.html" %} 2 | 3 | {% block container_class_name %}dashboard settings{%endblock%} 4 | 5 | {% block subcontainer %} 6 | 7 | 32 | 33 |
34 |
35 | {% block container-right %}{% endblock %} 36 |
37 |
38 | 39 | {% endblock %} 40 | 41 | -------------------------------------------------------------------------------- /templates/team/timeline.html: -------------------------------------------------------------------------------- 1 | {% extends "team/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 |
6 |

请等待...

7 | loader 8 |
9 |
10 |
11 | {% endblock %} 12 | 13 | {% block js %} 14 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/user/active.html: -------------------------------------------------------------------------------- 1 | {% extends "user/user_base.html" %} 2 | 3 | {% block primary %} 4 |
5 |
6 |

最近活跃

7 |
8 |
9 |
10 | {% endblock %} 11 | {% block subjs %} 12 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /templates/user/dashboard_base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block container_class_name %}dashboard{% endblock %} 4 | 5 | {% block container %} 6 | 7 |
8 |
9 |
19 |
20 | 21 |
22 |
23 | {% block subcontainer %}{% endblock %} 24 |
25 |
26 | 27 | {% endblock %} 28 | 29 | {% block js %} 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/user/feed.html: -------------------------------------------------------------------------------- 1 | {% extends "user/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 |
6 |

请等待...

7 | loader 8 |
9 |
10 |
11 | {% endblock %} 12 | 13 | {% block js %} 14 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/user/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body_class_name %}authorize{% endblock %} 4 | 5 | {% block container %} 6 | 7 |
8 |
9 |

登录

10 |
11 |
12 | 13 |
14 |
15 |
16 | 使用 GitHub 账户登录 17 |
18 | {% if error %} 19 |
20 | 21 |

出错了! {{ error }}

22 |
23 | {% endif %} 24 |
25 |
26 | {% csrf_token %} 27 | {{ loginForm.email }} 28 | 29 | {{ loginForm.password }} 30 | 31 | 32 |
33 |
34 |

找回密码

35 |
{# auth-container #} 36 |
{# container #} 37 |
{# content #} 38 | 39 | {% endblock %} 40 | 41 | {% block js %} 42 | 54 | {% endblock %} 55 | -------------------------------------------------------------------------------- /templates/user/pull_merge.html: -------------------------------------------------------------------------------- 1 | {% extends "user/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 | 5 |
6 |
7 |

需要我处理的

8 | 9 | 需要我处理的 10 | 我创建的 11 | 12 | {% comment %} 13 | 14 | 进行中 15 | 已处理 16 | 17 | {% endcomment %} 18 |
19 | 20 | {% if pullRequests %} 21 | {% for pullRequest in pullRequests %} 22 |
23 |
24 | {{pullRequest.status_view}} 25 | 26 | {{pullRequest.source_refname}} 27 | 28 | {{pullRequest.desc_refname}} 29 | 30 |
31 |
32 |

33 | {{pullRequest.short_title}} 34 | #{{pullRequest.id}} 35 |

36 |
37 |

{{pullRequest.short_desc}}

38 |
39 |
40 | {{pullRequest.pull_user.username}} 41 | 42 |
43 |
44 |
45 | {% endfor %} 46 | 47 | {% else %} 48 |
49 |

没有合并请求需要处理

50 |
51 | {% endif %} 52 | 53 |
54 | 55 | 56 | {% endblock %} 57 | 58 | {% block js %} 59 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /templates/user/pull_request.html: -------------------------------------------------------------------------------- 1 | {% extends "user/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 |
6 |

我创建的

7 | 8 | 需要我处理的 9 | 我创建的 10 | 11 | {% comment %} 12 | 13 | 进行中 14 | 已处理 15 | 16 | {% endcomment %} 17 |
18 | 19 | {% if pullRequests %} 20 | {% for pullRequest in pullRequests %} 21 |
22 |
23 | {{pullRequest.status_view}} 24 | 25 | {{pullRequest.source_refname|truncatechars:10}} 26 | 27 | {{pullRequest.desc_refname}} 28 | 29 |
30 |
31 |

32 | {{pullRequest.short_title}} 33 | #{{pullRequest.id}} 34 |

35 |
36 |

{{pullRequest.short_desc}}

37 |
38 |
39 | {{pullRequest.desc_repo.username}} 40 | {{pullRequest.desc_repo.username}} 41 | 42 |
43 |
44 |
45 | {% endfor %} 46 | 47 | 48 | {% else %} 49 |
50 |

没有合并请求

51 |
52 | {% endif %} 53 |
54 | 55 | 56 | {% endblock %} 57 | 58 | {% block js %} 59 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /templates/user/recommend.html: -------------------------------------------------------------------------------- 1 | {% extends "user/user_base.html" %} 2 | 3 | {% block primary %} 4 | 5 |
6 |

评论

7 | {% for recommend in recommends %} 8 |
9 |
{{ recommend.username}}
10 |
11 |

{{recommend.username}}

12 |

{{recommend.content}}

13 | 14 | 15 | 确定删除该评论? 16 | 17 |
18 |
19 | {% endfor %} 20 |
21 | {% endblock %} 22 | {% block subjs %} 23 | 62 | {% endblock %} 63 | -------------------------------------------------------------------------------- /templates/user/shared/notif_nav.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | -------------------------------------------------------------------------------- /templates/user/star_repo.html: -------------------------------------------------------------------------------- 1 | {% extends "user/user_base.html" %} 2 | 3 | {% block primary %} 4 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% for star_repo in star_repos %} 17 | 18 | 19 | 31 | 32 | {% endfor %} 33 | 34 |
仓库简介
{{star_repo.name}} 20 | {{star_repo.desc}} 21 | 22 | 23 | {% if star_repo.visibly != 0 %} 24 | 已经删除 25 | {% endif %} 26 | {% if star_repo.auth_type == 2 %} 27 | 私有仓库 28 | {% endif %} 29 | 30 |
35 |
36 | {% endblock %} 37 | {% block subjs %} 38 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /templates/user/timeline.html: -------------------------------------------------------------------------------- 1 | {% extends "user/dashboard_base.html" %} 2 | 3 | {% block subcontainer %} 4 |
5 |
6 |

请等待...

7 | loader 8 |
9 |
10 |
11 | {% endblock %} 12 | 13 | {% block js %} 14 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/user/watch_repo.html: -------------------------------------------------------------------------------- 1 | {% extends "user/user_base.html" %} 2 | 3 | {% block primary %} 4 | 5 |
6 |
7 |

关注仓库

8 |
27 |
28 |
29 | {% endblock %} 30 | {% block subjs %} 31 | 63 | {% endblock %} 64 | -------------------------------------------------------------------------------- /templates/user/watch_user.html: -------------------------------------------------------------------------------- 1 | {% extends "user/user_base.html" %} 2 | 3 | {% block primary %} 4 | 5 |
6 |

关注

7 | {% for watch_user in watch_users %} 8 |
9 |
10 |
11 |

{{watch_user.username}}

12 |

{{watch_user.tweet}}

13 |
14 |
15 | {% endfor %} 16 | 17 |

被关注

18 | {% for bewatch_user in bewatch_users %} 19 |
20 |
21 |
22 |

{{bewatch_user.username}}

23 |

{{bewatch_user.tweet}}

24 |
25 |
26 | {% endfor %} 27 |
28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /thirdparty/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/thirdparty/__init__.py -------------------------------------------------------------------------------- /thirdparty/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /thirdparty/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /todolist/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/todolist/__init__.py -------------------------------------------------------------------------------- /todolist/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /viewtools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/viewtools/__init__.py -------------------------------------------------------------------------------- /viewtools/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | -------------------------------------------------------------------------------- /viewtools/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudzhou/gitshell/2832336fabee3ea46b5e78da1b2498875f65c303/viewtools/templatetags/__init__.py -------------------------------------------------------------------------------- /viewtools/templatetags/gstools.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from gitshell.gsuser.utils import UrlRouter 3 | 4 | register = template.Library() 5 | 6 | @register.filter 7 | def keyvalue(dict, key): 8 | return dict[key] 9 | 10 | @register.filter 11 | def route(urlRouter, url): 12 | return urlRouter.route(url) 13 | 14 | @register.filter 15 | def route_context(urlRouter, url): 16 | return urlRouter.route_context(url) 17 | 18 | 19 | -------------------------------------------------------------------------------- /viewtools/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /viewtools/views.py: -------------------------------------------------------------------------------- 1 | import time, json, functools 2 | from sets import Set 3 | from datetime import datetime 4 | from django.utils.html import escape 5 | from django.http import HttpResponse, HttpResponseRedirect, Http404 6 | from django.contrib.auth.models import User, UserManager, check_password 7 | from gitshell.repo.models import Repo 8 | 9 | reserve_field = { 10 | type(Repo()): ['deploy_url', 'dropbox_url'], 11 | type(User()): ['password'], 12 | } 13 | def json_httpResponse(o): 14 | return json_httpResponse_obj2dict(o, True, True) 15 | 16 | def json_success(message): 17 | return json_httpResponse({'code': 200, 'result': 'success', 'message': message}) 18 | 19 | def json_failed(code, message): 20 | return json_httpResponse({'code': code, 'result': 'failed', 'message': message}) 21 | 22 | def json_httpResponse_obj2dict(o, is_obj2dict, is_safe): 23 | if is_obj2dict: 24 | o = obj2dict(o, is_safe) 25 | return HttpResponse(json_escape_dumps(o), mimetype='application/json') 26 | 27 | def json_escape_dumps(o): 28 | #json.encoder.encode_basestring = encoder 29 | ##json.encoder.encode_basestring_ascii = functools.partial(encoder, _encoder=json.encoder.encode_basestring_ascii) 30 | #json.encoder.encode_basestring_ascii = encoder 31 | return json.dumps(o) 32 | 33 | def encoder(o, _encoder=json.encoder.encode_basestring): 34 | if isinstance(o, basestring): 35 | o = escape(o) 36 | return _encoder(o) 37 | 38 | def obj2dict(obj, is_safe): 39 | id_dict = {} 40 | return _recursion_obj2dict(id_dict, obj, True) 41 | 42 | def _recursion_obj2dict(id_dict, obj, is_safe): 43 | if obj is None: 44 | return None 45 | if isinstance(obj, basestring): 46 | return escape(obj) 47 | if isinstance(obj, (int, long, float, complex)): 48 | return obj 49 | if isinstance(obj, datetime): 50 | return time.mktime(obj.timetuple()) 51 | if isinstance(obj, list): 52 | lobj = [] 53 | for x in list(obj): 54 | lobj.append(_recursion_obj2dict(id_dict, x, is_safe)) 55 | return lobj 56 | if isinstance(obj, dict): 57 | dobj = {} 58 | for key, value in dict(obj).items(): 59 | dobj[_recursion_obj2dict(id_dict, key, is_safe)] = _recursion_obj2dict(id_dict, value, is_safe) 60 | return dobj 61 | if isinstance(obj, object): 62 | uuid = id(obj) 63 | if uuid in id_dict: 64 | return id_dict[uuid] 65 | obj_type = type(obj) 66 | _dict = {} 67 | for key, value in obj.__dict__.iteritems(): 68 | if is_safe and obj_type in reserve_field: 69 | if key in reserve_field[obj_type]: 70 | continue 71 | if not key.startswith('_') and not callable(value): 72 | _dict[_recursion_obj2dict(id_dict, key, is_safe)] = _recursion_obj2dict(id_dict, value, is_safe) 73 | id_dict[uuid] = _dict 74 | return _dict 75 | return obj 76 | 77 | 78 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import django.core.handlers.wsgi 4 | 5 | #sys.path.append(os.path.abspath(os.path.dirname(__file__))) 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gitshell.settings") 7 | application = django.core.handlers.wsgi.WSGIHandler() 8 | 9 | 10 | --------------------------------------------------------------------------------