├── .gitignore
├── htdocs
├── sage_logo_trac_v2.png
└── theme.css
├── templates
├── prefs_ssh_keys.html
└── ticket_box.html
└── plugins
├── trac_plugin_search_branch.py
├── sshkeys.py
└── ticket_branch.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | log
3 | files
4 |
--------------------------------------------------------------------------------
/htdocs/sage_logo_trac_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sagemath/sage_trac/master/htdocs/sage_logo_trac_v2.png
--------------------------------------------------------------------------------
/templates/prefs_ssh_keys.html:
--------------------------------------------------------------------------------
1 |
4 |
7 |
8 |
9 | You can associate up to 256 ssh keys to your trac account.
13 | You can copy-paste them to the text area below, each on its own line.
14 |
20 |
21 |
22 |
Opened ${pretty_dateinfo(ticket.time)}
23 |
Closed ${pretty_dateinfo(closetime)}
24 |
25 | Last modified ${pretty_dateinfo(ticket.changetime)}
26 |
(ticket not yet created)
27 |
28 |
29 |
30 |
31 |
35 |
40 |
41 |
42 |
43 |
44 |
46 | #${ticket.id}
47 |
48 |
49 |
50 | ${'status' in fields_map and fields[fields_map['status']].rendered or ticket.status}
51 |
52 |
53 | ${'type' in fields_map and fields[fields_map['type']].rendered or ticket.type}
54 |
55 |
56 | (${'resolution' in fields_map and fields[fields_map['resolution']].rendered or ticket.resolution})
57 |
58 |
59 |
60 |
61 | $ticket.summary
62 |
63 |
64 |
65 | — at Initial Version
66 |
67 |
68 | — at Version $version
69 |
70 |
71 |
72 |
73 |
75 |
79 | | Reported by: |
80 | $v_reporter |
81 | Owned by: |
82 | $v_owner |
83 |
84 |
86 |
87 | |
92 | ${field.label or field.name}:
93 | |
94 |
98 |
99 |
100 | ${pretty_dateinfo(field.dateinfo, field.format)}
101 | ${field.rendered}
102 | ${ticket[field.name]}
103 |
104 |
105 | |
106 |
107 |
108 |
109 |
110 |
117 |
118 |
119 |
126 |
127 | ${wiki_to_html(context, ticket.description, escape_newlines=preserve_newlines)}
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/plugins/sshkeys.py:
--------------------------------------------------------------------------------
1 | from trac.core import *
2 | from trac.web.chrome import *
3 | from trac.util.translation import gettext as _
4 | from trac.prefs import IPreferencePanelProvider
5 | from trac.admin.api import IAdminCommandProvider
6 | from trac.util.text import printout
7 | from tracrpc.api import IXMLRPCHandler
8 | import subprocess, os
9 |
10 | _home = '/home/www-data'
11 | _gitolite_keydir = os.path.join(_home, 'gitolite', 'keydir')
12 | _gitolite_update = os.path.join(_home, 'bin', 'gitolite-update')
13 |
14 | class UserDataStore(Component):
15 | def save_data(self, user, dictionary):
16 | """
17 | Saves user data for user.
18 | """
19 | self._create_table()
20 | with self.env.db_transaction as db:
21 | cursor = db.cursor()
22 | for key, value in dictionary.iteritems():
23 | cursor.execute('DELETE FROM "user_data_store" WHERE "user"=%s', (user,))
24 | cursor.execute('INSERT INTO "user_data_store" VALUES (%s, %s, %s)', (user, key, value))
25 |
26 | def get_data(self, user):
27 | """
28 | Returns a dictionary with all data keys
29 | """
30 | self._create_table()
31 | with self.env.db_query as db:
32 | cursor = db.cursor()
33 | cursor.execute('SELECT key, value FROM "user_data_store" WHERE "user"=%s', (user,))
34 | return {key:value for key, value in cursor}
35 |
36 | def get_data_all_users(self):
37 | """
38 | Returns a dictionary with all data keys
39 | """
40 | self._create_table()
41 | return_value = {}
42 | with self.env.db_query as db:
43 | cursor = db.cursor()
44 | cursor.execute('SELECT "user", key, value FROM "user_data_store"')
45 | for user, key, value in cursor:
46 | if return_value.has_key(user):
47 | return_value[user][key] = value
48 | else:
49 | return_value[user] = {key: value}
50 | return return_value
51 |
52 | def _create_table(self):
53 | with self.env.db_transaction as db:
54 | cursor = db.cursor()
55 | cursor.execute('SELECT * FROM information_schema.tables WHERE "table_name"=%s', ('user_data_store',))
56 | if not cursor.rowcount:
57 | cursor.execute('CREATE TABLE "user_data_store" ( "user" text, key text, value text, UNIQUE ( "user", key ) )')
58 |
59 | class SshKeysPlugin(Component):
60 | implements(IPreferencePanelProvider, IAdminCommandProvider, IXMLRPCHandler)
61 |
62 | def __init__(self):
63 | self._user_data_store = UserDataStore(self.compmgr)
64 |
65 | # IPreferencePanelProvider methods
66 | def get_preference_panels(self, req):
67 | yield ('sshkeys', _('SSH keys'))
68 |
69 | def render_preference_panel(self, req, panel):
70 | if req.method == 'POST':
71 | new_ssh_keys = set(key.strip() for key in req.args.get('ssh_keys').splitlines())
72 | if new_ssh_keys:
73 | self.setkeys(req, new_ssh_keys)
74 | add_notice(req, 'Your ssh key has been saved.')
75 | req.redirect(req.href.prefs(panel or None))
76 |
77 | return 'prefs_ssh_keys.html', self._user_data_store.get_data(req.authname)
78 |
79 | # IAdminCommandProvider methods
80 | def get_admin_commands(self):
81 | yield ('sshkeys listusers', '',
82 | 'Get a list of users that have a SSH key registered',
83 | None, self._do_listusers)
84 | yield ('sshkeys dumpkey', '