├── .cico.pipeline
├── CHANGES.txt
├── github2fedmsg
├── templates
│ ├── widget.mak
│ ├── index.mak
│ ├── __init__.py
│ └── master.mak
├── static
│ ├── favicon.ico
│ ├── footerbg.png
│ ├── headerbg.png
│ ├── middlebg.png
│ ├── pyramid.png
│ ├── transparent.gif
│ ├── github2fedmsg.png
│ ├── pyramid-small.png
│ ├── bootstrap-3.1.1-fedora
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ └── glyphicons-halflings-regular.woff
│ │ └── css
│ │ │ ├── bootstrap-theme.min.css
│ │ │ └── bootstrap-theme.css
│ ├── ie6.css
│ ├── messenger-1.4.1
│ │ ├── LICENSE
│ │ ├── js
│ │ │ ├── messenger-theme-flat.js
│ │ │ ├── messenger-theme-future.js
│ │ │ └── messenger.min.js
│ │ └── css
│ │ │ ├── messenger.css
│ │ │ ├── messenger-theme-block.css
│ │ │ ├── messenger-theme-ice.css
│ │ │ ├── messenger-spinner.css
│ │ │ ├── messenger-theme-air.css
│ │ │ ├── messenger-theme-flat.css
│ │ │ └── messenger-theme-future.css
│ └── github2fedmsg.css
├── widgets
│ ├── templates
│ │ ├── __init__.py
│ │ └── profile.mak
│ ├── __init__.py
│ ├── static
│ │ └── profile.js
│ └── users.py
├── scripts
│ ├── __init__.py
│ └── initializedb.py
├── events.py
├── tests.py
├── views
│ ├── __init__.py
│ ├── auth.py
│ └── webhooks.py
├── custom_openid.py
├── traversal.py
├── models
│ ├── jsonifiable.py
│ └── __init__.py
├── githubutils.py
└── __init__.py
├── .gitignore
├── MANIFEST.in
├── tox.ini
├── fedora-messaging.toml.example
├── secret.ini.example
├── TODO.md
├── README.rst
├── development.ini
├── setup.py
├── tools
└── fake-github-post.py
└── CHANGELOG.rst
/.cico.pipeline:
--------------------------------------------------------------------------------
1 | fedoraInfraTox { }
2 |
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | 0.0
2 | ---
3 |
4 | - Initial version
5 |
--------------------------------------------------------------------------------
/github2fedmsg/templates/widget.mak:
--------------------------------------------------------------------------------
1 | <%inherit file="master.mak"/>
2 | ${widget.display() | n}
3 |
--------------------------------------------------------------------------------
/github2fedmsg/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/favicon.ico
--------------------------------------------------------------------------------
/github2fedmsg/static/footerbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/footerbg.png
--------------------------------------------------------------------------------
/github2fedmsg/static/headerbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/headerbg.png
--------------------------------------------------------------------------------
/github2fedmsg/static/middlebg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/middlebg.png
--------------------------------------------------------------------------------
/github2fedmsg/static/pyramid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/pyramid.png
--------------------------------------------------------------------------------
/github2fedmsg/static/transparent.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/transparent.gif
--------------------------------------------------------------------------------
/github2fedmsg/static/github2fedmsg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/github2fedmsg.png
--------------------------------------------------------------------------------
/github2fedmsg/static/pyramid-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/pyramid-small.png
--------------------------------------------------------------------------------
/github2fedmsg/static/bootstrap-3.1.1-fedora/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/bootstrap-3.1.1-fedora/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/github2fedmsg/static/bootstrap-3.1.1-fedora/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/bootstrap-3.1.1-fedora/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/github2fedmsg/static/bootstrap-3.1.1-fedora/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fedora-infra/github2fedmsg/develop/github2fedmsg/static/bootstrap-3.1.1-fedora/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.swp
3 | ez_setup
4 | tw2*
5 | *.db*
6 | data
7 | build
8 | dist
9 | doc/build
10 | *.egg*
11 | README.pdf
12 | *.pid
13 | *.log
14 | github2fedmsg/githubsecrets.*
15 | web198.ini
16 | testlog.txt
17 | secret.ini
18 | .tox
19 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt development.ini alembic.ini secret.ini.example *.cfg *.rst LICENSE
2 | prune secret.ini
3 | graft github2fedmsg/static
4 | graft github2fedmsg/templates
5 | graft github2fedmsg/widgets/templates
6 | graft github2fedmsg/widgets/static
7 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27
3 | skip_missing_interpreters = True
4 |
5 | [testenv]
6 | commands =
7 | python -m unittest -v {posargs:github2fedmsg.tests}
8 | passenv = HOME
9 |
10 | [testenv:lint]
11 | basepython = python3
12 | commands =
13 | python -m flake8 github2fedmsg
14 | deps =
15 | flake8>3.0
16 |
--------------------------------------------------------------------------------
/fedora-messaging.toml.example:
--------------------------------------------------------------------------------
1 | # Example configuraton for Fedora Messaging
2 |
3 | # Broker address
4 | amqp_url = "amqp://"
5 |
6 | # Authentication is TLS-based
7 | [tls]
8 | ca_cert = "/etc/pki/tls/certs/ca-bundle.crt"
9 | keyfile = "/my/client/key.pem"
10 | certfile = "/my/client/cert.pem"
11 |
12 | [client_properties]
13 | app = "github2fedmsg"
14 |
--------------------------------------------------------------------------------
/secret.ini.example:
--------------------------------------------------------------------------------
1 | [github2fedmsg]
2 |
3 | # This is the "application" key that is used to negotiate oauth on behalf of
4 | # other users: https://github.com/settings/applications/new
5 | velruse.github.consumer_secret = create-your-own
6 |
7 | # This is our "own" oauth bearer token that we use to make authenticated github
8 | # queries: https://github.com/settings/tokens/new
9 | github.secret_oauth_access_token = create-your-own
10 |
--------------------------------------------------------------------------------
/github2fedmsg/templates/index.mak:
--------------------------------------------------------------------------------
1 | <%inherit file="master.mak"/>
2 |
3 |
16 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | - [X] clean out the model to only what we need
2 | - [X] Add FAS <-> Github account linking.
3 | - [X] set up irc notifications to #fedora-apps for development
4 | - [X] Remove pygithub3, replace with our own lib.
5 | - [X] Remove pep8bot branding
6 | - [X] Style with robyduck's latest bootstrap fedora
7 | - [X] Add fedmsg publication code, test it.
8 | - [X] Set up a cloud node for shared dev/testing
9 | - [X] Write fedmsg.meta processors
10 | - [X] Package it up; package review.
11 | - [X] Take another pass through the UI and clean it up. @ryanlerch is working
12 | on a nicer design; in the meantime we can spruce it up ourselves.
13 | - [X] Staging/ansible
14 | - [X] Production
15 |
--------------------------------------------------------------------------------
/github2fedmsg/static/ie6.css:
--------------------------------------------------------------------------------
1 | * html img,
2 | * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
3 | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
4 | this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
5 | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
6 | this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
7 | );}
8 | #wrap{display:table;height:100%}
9 |
--------------------------------------------------------------------------------
/github2fedmsg/templates/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 |
--------------------------------------------------------------------------------
/github2fedmsg/widgets/templates/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 |
--------------------------------------------------------------------------------
/github2fedmsg/scripts/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | # package
18 |
--------------------------------------------------------------------------------
/github2fedmsg/widgets/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from .users import (
18 | UserProfile,
19 | )
20 |
--------------------------------------------------------------------------------
/github2fedmsg/widgets/static/profile.js:
--------------------------------------------------------------------------------
1 | function subscribe(link) {
2 | $.ajax(link, {
3 | success: function(json, stat, xhr) {
4 | var name, sel;
5 | name = json.repo.name;
6 | name = name.replace(/\./g, '\\.');
7 | sel = $("#"+json.github_username+'-'+name);
8 | sel.toggleClass('btn-success');
9 | sel.toggleClass('btn-default');
10 |
11 | if (json.repo.enabled) {
12 | sel.html("On");
13 | } else {
14 | sel.html("Off");
15 | }
16 | },
17 | error: function(json, stat, xhr) {
18 | Messenger({theme: 'flat'}).post({
19 | message: "Sorry. There was an error on the server.",
20 | type: "error",
21 | });
22 | console.log('error');
23 | console.log(json);
24 | },
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 HubSpot, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
9 |
--------------------------------------------------------------------------------
/github2fedmsg/static/github2fedmsg.css:
--------------------------------------------------------------------------------
1 |
2 | .profile-header h1 {
3 |
4 | padding-left: 25px;
5 | }
6 |
7 | .profile-header form {
8 | display: inline-block;
9 | }
10 |
11 | .profile-buttons {
12 | margin-top: -20px;
13 | margin-right: 20px;
14 | }
15 |
16 | .vspace {
17 | height: 40px;
18 | }
19 |
20 | .content {
21 | margin-left: auto;
22 | margin-right: auto;
23 | width: 100%;
24 | /*width: 0px;*/
25 | }
26 |
27 | .logo {
28 | margin-top: 20px;
29 | margin-bottom: 20px;
30 | margin-left: auto;
31 | margin-right: auto;
32 | display: inline-block;
33 | }
34 |
35 | form.navbar-form {
36 | margin-left: 50px;
37 | padding-top: 2px;
38 | }
39 |
40 | .masthead {
41 | margin-top: 140px;
42 | font-size: 120%;
43 | }
44 | .masthead h1 {
45 | font-size: 64pt;
46 | }
47 | .masthead .lead {
48 | font-size: 28pt;
49 | }
50 |
51 |
52 | /* Sticky footer styles */
53 | html,
54 | body {
55 | height: 100%;
56 | }
57 |
58 | .max-width {
59 | min-width: 100%;
60 | }
61 |
62 | #wrap {
63 | min-height: 100%;
64 | height: auto;
65 | margin: 0 auto -45px;
66 | padding: 0 0 60px;
67 | }
68 |
69 | #footer {
70 | height: 40px;
71 | background-color: #f5f5f5;
72 | border-top: 1px dashed black;
73 | text-align: center;
74 | }
75 |
76 | #footer {
77 | padding: 10px;
78 | }
79 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/js/messenger-theme-flat.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var $, FlatMessage, spinner_template,
3 | __hasProp = {}.hasOwnProperty,
4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
5 |
6 | $ = jQuery;
7 |
8 | spinner_template = '\n \n \n \n \n \n \n
';
9 |
10 | FlatMessage = (function(_super) {
11 |
12 | __extends(FlatMessage, _super);
13 |
14 | function FlatMessage() {
15 | return FlatMessage.__super__.constructor.apply(this, arguments);
16 | }
17 |
18 | FlatMessage.prototype.template = function(opts) {
19 | var $message;
20 | $message = FlatMessage.__super__.template.apply(this, arguments);
21 | $message.append($(spinner_template));
22 | return $message;
23 | };
24 |
25 | return FlatMessage;
26 |
27 | })(window.Messenger.Message);
28 |
29 | window.Messenger.themes.flat = {
30 | Message: FlatMessage
31 | };
32 |
33 | }).call(this);
34 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/js/messenger-theme-future.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var $, FutureMessage, spinner_template,
3 | __hasProp = {}.hasOwnProperty,
4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
5 |
6 | $ = jQuery;
7 |
8 | spinner_template = '\n \n \n \n \n \n \n
';
9 |
10 | FutureMessage = (function(_super) {
11 |
12 | __extends(FutureMessage, _super);
13 |
14 | function FutureMessage() {
15 | return FutureMessage.__super__.constructor.apply(this, arguments);
16 | }
17 |
18 | FutureMessage.prototype.template = function(opts) {
19 | var $message;
20 | $message = FutureMessage.__super__.template.apply(this, arguments);
21 | $message.append($(spinner_template));
22 | return $message;
23 | };
24 |
25 | return FutureMessage;
26 |
27 | })(window.Messenger.Message);
28 |
29 | window.Messenger.themes.future = {
30 | Message: FutureMessage
31 | };
32 |
33 | }).call(this);
34 |
--------------------------------------------------------------------------------
/github2fedmsg/events.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from pyramid.threadlocal import get_current_request
18 | from pyramid.events import subscriber
19 | from pyramid.events import BeforeRender
20 | from pyramid.security import authenticated_userid
21 |
22 | import tw2.core
23 |
24 |
25 | def when_ready(func):
26 | """
27 | Takes a js_function and returns a js_callback that will run
28 | when the document is ready.
29 | """
30 | return tw2.core.js_callback(
31 | '$(document).ready(function(){' + str(func) + '});'
32 | )
33 |
34 |
35 | @subscriber(BeforeRender)
36 | def inject_globals(event):
37 | request = get_current_request()
38 |
39 | # Expose these as global attrs for our templates
40 | event['identity'] = authenticated_userid(request)
41 |
42 | when_ready(
43 | "$('.dropdown-toggle').dropdown();"
44 | )
45 |
--------------------------------------------------------------------------------
/github2fedmsg/scripts/initializedb.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import os
18 | import sys
19 | import transaction
20 |
21 | from sqlalchemy import engine_from_config
22 |
23 | from pyramid.paster import (
24 | get_appsettings,
25 | setup_logging,
26 | )
27 |
28 | from ..models import (
29 | DBSession,
30 | Base,
31 | )
32 |
33 | def usage(argv):
34 | cmd = os.path.basename(argv[0])
35 | print('usage: %s \n'
36 | '(example: "%s development.ini")' % (cmd, cmd))
37 | sys.exit(1)
38 |
39 | def main(argv=sys.argv):
40 | if len(argv) != 2:
41 | usage(argv)
42 |
43 | config_uri = argv[1]
44 | setup_logging(config_uri)
45 | settings = get_appsettings(config_uri, name="github2fedmsg")
46 | engine = engine_from_config(settings, 'sqlalchemy.')
47 | DBSession.configure(bind=engine)
48 | Base.metadata.create_all(engine)
49 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | github2fedmsg
2 | -------------
3 |
4 | A bot broadcasts every action made on your repo hosted on GitHub on the
5 | `fedora-messaging `_ message bus.
6 |
7 | It is a web application that monitors GitHub repositories you subscribe it to.
8 | When new actions (commits, pull-request, tickets) are made, it broadcasts a
9 | message on the `fedora-messaging`_ message bus.
10 |
11 | You can see all the current messages with `datagrepper's "github" category
12 | `_.
13 |
14 | It is written in Python on the Pyramid framework, and uses `velruse
15 | `_ to talk with GitHub. It adds a webhook callback
16 | back to itself on repositories you ask it to monitor. When one of those
17 | callbacks fire, github2fedmsg republishes the message it receives to the
18 | `fedora-messaging`_ bus.
19 |
20 | Hacking
21 | -------
22 |
23 | If you run into trouble with these instructions, feel free to open a ticket
24 | or get in touch with me directly.
25 |
26 | Fork and clone the following two repositories:
27 |
28 | - http://github.com/fedora-infra/github2fedmsg
29 |
30 | Using `virtualenvwrapper `_::
31 |
32 | $ cd github2fedmsg
33 | $ mkvirtualenv github2fedmsg
34 | $ python setup.py develop
35 | $ pip install waitress
36 |
37 | Go off and `register your development application with GitHub
38 | `_. Save the oauth tokens and add
39 | the secret one to a new file you create called ``secret.ini``. Use the example
40 | ``secret.ini.example`` file.
41 |
42 |
43 | Create the database::
44 |
45 | $ initialize_github2fedmsg_db development.ini
46 |
47 |
48 | Now, start the webapp::
49 |
50 | $ workon github2fedmsg
51 | $ pserve development.ini --reload
52 |
--------------------------------------------------------------------------------
/development.ini:
--------------------------------------------------------------------------------
1 | [app:github2fedmsg]
2 | use = egg:github2fedmsg
3 |
4 | pyramid.reload_templates = true
5 | pyramid.default_locale_name = en
6 | pyramid.includes =
7 | pyramid_tm
8 |
9 | sqlalchemy.url = sqlite:///%(here)s/github2fedmsg.db
10 |
11 | mako.directories = github2fedmsg:templates
12 |
13 | # github2fedmsg developer key.
14 | velruse.github.consumer_key = ee676d3349157a49fbbd
15 | velruse.github.scope = read:org,admin:repo_hook
16 |
17 | # For talking with FAS
18 | velruse.openid.identifier = https://id.fedoraproject.org/
19 | velruse.openid.realm = http://127.0.0.1:6543
20 |
21 | # By default, github cannot actually reach you here.
22 | github.callback = http://127.0.0.1:6543/webhook
23 | github.secret = changeme!
24 |
25 | session.secret = changeme2
26 | authnsecret = changeme3
27 |
28 | [pipeline:main]
29 | pipeline =
30 | egg:WebError#evalerror
31 | tw2
32 | github2fedmsg
33 |
34 | [filter:tw2]
35 | use = egg:tw2.core#middleware
36 |
37 | [server:main]
38 | use = egg:waitress#main
39 | host = 127.0.0.1
40 | port = 6543
41 |
42 | # Begin logging configuration
43 |
44 | [loggers]
45 | keys = root, github2fedmsg, sqlalchemy
46 |
47 | [handlers]
48 | keys = console
49 |
50 | [formatters]
51 | keys = generic
52 |
53 | [logger_root]
54 | level = INFO
55 | handlers = console
56 |
57 | [logger_github2fedmsg]
58 | level = DEBUG
59 | handlers =
60 | qualname = github2fedmsg
61 |
62 | [logger_sqlalchemy]
63 | level = WARN
64 | #level = INFO
65 | handlers =
66 | qualname = sqlalchemy.engine
67 | # "level = INFO" logs SQL queries.
68 | # "level = DEBUG" logs SQL queries and results.
69 | # "level = WARN" logs neither. (Recommended for production systems.)
70 |
71 | [handler_console]
72 | class = StreamHandler
73 | args = (sys.stderr,)
74 | level = NOTSET
75 | formatter = generic
76 |
77 | [formatter_generic]
78 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
79 |
80 | # End logging configuration
81 |
--------------------------------------------------------------------------------
/github2fedmsg/tests.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import unittest
18 | import transaction
19 |
20 | from pyramid import testing
21 | from sqlalchemy import create_engine
22 |
23 | from .models import DBSession, Base, Repo
24 | from .views import home, widget_view
25 |
26 | class TestMyView(unittest.TestCase):
27 | def setUp(self):
28 | self.config = testing.setUp()
29 | engine = create_engine('sqlite://')
30 | DBSession.configure(bind=engine)
31 | Base.metadata.create_all(engine)
32 | with transaction.manager:
33 | repo = Repo(name='testrepo', description="Test Repo", language="cobol")
34 | DBSession.add(repo)
35 |
36 | def tearDown(self):
37 | DBSession.remove()
38 | testing.tearDown()
39 |
40 | def test_home(self):
41 | request = testing.DummyRequest()
42 | request.user = None
43 | result = home(request)
44 | self.assertEqual(result, {})
45 |
46 | def test_widget_view(self):
47 | request = testing.DummyRequest()
48 | request.user = None
49 | result = widget_view(request)
50 | self.assertEqual(result, {'widget': None})
51 |
--------------------------------------------------------------------------------
/github2fedmsg/views/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from pyramid.view import view_config
18 | from pyramid.security import authenticated_userid
19 | from pyramid.httpexceptions import HTTPFound, HTTPUnauthorized, HTTPForbidden
20 |
21 | import github2fedmsg.models as m
22 |
23 |
24 | @view_config(route_name='home', renderer='index.mak')
25 | def home(request):
26 | if request.user:
27 | return HTTPFound(location=request.user.username)
28 | return {}
29 |
30 |
31 | @view_config(name='sync', context=m.User, renderer='json')
32 | def sync_user(request):
33 | # TODO -- someday, learn how to do the __acls__ thing.. :/
34 | username = request.context.username
35 | userid = authenticated_userid(request)
36 | if userid != username:
37 | raise HTTPUnauthorized()
38 |
39 | config_key = 'github.secret_oauth_access_token'
40 | value = request.registry.settings[config_key]
41 | oauth_creds = dict(access_token=value)
42 |
43 | import transaction
44 | request.context.sync_repos(oauth_creds)
45 | transaction.commit()
46 | home = request.route_url('home')
47 | raise HTTPFound(home + username)
48 |
49 |
50 | @view_config(context="tw2.core.widgets.WidgetMeta",
51 | renderer='widget.mak')
52 | def widget_view(request):
53 | return dict(widget=request.context)
54 |
--------------------------------------------------------------------------------
/github2fedmsg/templates/master.mak:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | github2fedmsg - rebroadcast GitHub events to the fedmsg bus
5 |
6 |
8 |
10 |
12 |
13 |
15 |
17 |
19 |
21 |
22 |
23 |
24 | ${self.body()}
25 |
26 |
27 |
37 |
38 | % if request.registry.settings.get('fedmenu.url'):
39 |
40 |
47 | % endif
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/github2fedmsg/custom_openid.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import velruse.api
18 | import velruse.providers.openid as vr
19 |
20 | from pyramid.security import NO_PERMISSION_REQUIRED
21 |
22 |
23 | def add_openid_login(config, realm, identity_provider):
24 | provider = SingleOpenIDConsumer(
25 | 'openid', 'openid',
26 | realm=realm,
27 | identity_provider=identity_provider,
28 | storage=None,
29 | )
30 | login_path='/login/openid'
31 | callback_path='/login/openid/callback'
32 | config.add_route(provider.login_route, login_path)
33 | config.add_view(provider, attr='login', route_name=provider.login_route,
34 | permission=NO_PERMISSION_REQUIRED)
35 | config.add_route(provider.callback_route, callback_path,
36 | use_global_views=True,
37 | factory=provider.callback)
38 | velruse.api.register_provider(config, 'openid', provider)
39 |
40 |
41 | class SingleOpenIDConsumer(vr.OpenIDConsumer):
42 | def __init__(self,
43 | name,
44 | _type,
45 | realm=None,
46 | identity_provider=None,
47 | storage=None,
48 | context=vr.OpenIDAuthenticationComplete):
49 | super(SingleOpenIDConsumer, self).__init__(
50 | name, _type, realm, storage, context)
51 | self.identity_provider = identity_provider
52 |
53 | def _lookup_identifier(self, request, url):
54 | return self.identity_provider
55 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import os
18 |
19 | from setuptools import setup, find_packages
20 |
21 | here = os.path.abspath(os.path.dirname(__file__))
22 | README = open(os.path.join(here, 'README.rst')).read()
23 | CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
24 |
25 | requires = [
26 | 'fedora-messaging',
27 | 'pyramid',
28 | 'SQLAlchemy',
29 | 'transaction',
30 | 'pyramid_tm',
31 | 'pyramid_mako',
32 | 'zope.sqlalchemy',
33 | 'weberror',
34 | 'velruse',
35 | 'tw2.core',
36 | ]
37 |
38 | setup(name='github2fedmsg',
39 | version='0.3.6',
40 | description='Pubsubhubbub app that rebroadcasts GitHub events over fedora-messaging',
41 | long_description=README + '\n\n' + CHANGES,
42 | classifiers=[
43 | "Programming Language :: Python",
44 | "Framework :: Pylons",
45 | "Topic :: Internet :: WWW/HTTP",
46 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
47 | ],
48 | author='Ralph Bean',
49 | author_email='rbean@redhat.com',
50 | url='https://github.com/fedora-infra/github2fedmsg',
51 | keywords='web wsgi bfg pylons pyramid',
52 | packages=find_packages(),
53 | include_package_data=True,
54 | zip_safe=False,
55 | test_suite='github2fedmsg',
56 | install_requires=requires,
57 | entry_points="""\
58 | [paste.app_factory]
59 | main = github2fedmsg:main
60 | [console_scripts]
61 | initialize_github2fedmsg_db = github2fedmsg.scripts.initializedb:main
62 | """,
63 | )
64 |
65 |
--------------------------------------------------------------------------------
/github2fedmsg/widgets/users.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import tw2.core as twc
18 | import github2fedmsg.models
19 | import pyramid.threadlocal
20 | from sqlalchemy import and_
21 |
22 |
23 | class UserProfile(twc.Widget):
24 | template = "mako:github2fedmsg.widgets.templates.profile"
25 | user = twc.Param("An instance of the User SQLAlchemy model.")
26 | resources = [
27 | twc.JSLink(filename="static/profile.js"),
28 | ]
29 |
30 | show_buttons = twc.Param("show my buttons?", default=False)
31 |
32 | def prepare(self):
33 | """ Query github for some information before display """
34 |
35 | oauth_creds = dict(access_token=self.user.oauth_access_token)
36 |
37 | # Try to refresh list of repos only if the user has none.
38 | if self.user.github_username and \
39 | self.user.oauth_access_token and \
40 | not self.user.all_repos:
41 | self.user.sync_repos(oauth_creds)
42 |
43 |
44 | def make_button(self, repo):
45 | # TODO -- Can we use resource_url here?
46 | username = repo.user.username
47 | github_username = repo.user.github_username
48 | home = self.request.route_url('home')
49 | link = home + 'api/%s/%s/%s/toggle' % (username, github_username, repo.name)
50 | click = 'onclick="subscribe(\'%s\')"' % link
51 |
52 | if repo.enabled:
53 | cls, text = "btn-success", "On"
54 | else:
55 | cls, text = "btn-default", "Off"
56 |
57 | return "%s " % (
58 | github_username, repo.name, cls, click, text)
59 |
60 | @property
61 | def request(self):
62 | return pyramid.threadlocal.get_current_request()
63 |
--------------------------------------------------------------------------------
/github2fedmsg/traversal.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from hashlib import md5
18 | import tw2.core as twc
19 |
20 | import github2fedmsg.models
21 | import github2fedmsg.widgets
22 | from pyramid.security import authenticated_userid
23 |
24 |
25 | def make_root(request):
26 | return RootApp(request)
27 |
28 |
29 | class RootApp(dict):
30 | __name__ = None
31 | __parent__ = None
32 |
33 | def __init__(self, request):
34 | dict.__init__(self)
35 | self.request = request
36 | self.static = dict(
37 | api=ApiApp(),
38 | )
39 |
40 | def __getitem__(self, key):
41 | if key in self.static:
42 | return self.static[key]
43 |
44 | query = github2fedmsg.models.User.query.filter_by(username=key)
45 | if query.count() != 1:
46 | raise KeyError("No such user")
47 |
48 | user = query.one()
49 |
50 | # TODO -- use __acl__ machinery some day
51 | userid = authenticated_userid(self.request)
52 | # TODO -- check if this is an org that I own
53 | show_buttons = (userid == user.username)
54 | return UserApp(user=user, show_buttons=show_buttons)
55 |
56 |
57 | class ApiApp(object):
58 | def __getitem__(self, key):
59 | query = github2fedmsg.models.User.query.filter_by(username=key)
60 | if query.count() != 1:
61 | raise KeyError("No such user")
62 | return query.one()
63 |
64 |
65 | class UserApp(github2fedmsg.widgets.UserProfile):
66 | __name__ = None
67 | __parent__ = RootApp
68 |
69 | @classmethod
70 | def __getitem__(self, key):
71 | for repo in self.user.repos:
72 | if repo.name == key:
73 | return repo
74 |
75 | raise KeyError
76 |
77 |
78 | class APISuccess(object):
79 | def __init__(self, data):
80 | self.data = data
81 |
--------------------------------------------------------------------------------
/github2fedmsg/widgets/templates/profile.mak:
--------------------------------------------------------------------------------
1 | ## -*- coding: utf-8 -*-
2 |
3 |
25 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Name
58 | Description
59 | Language
60 | % if w.show_buttons and w.user.oauth_access_token:
61 | Enabled?
62 | % endif
63 |
64 | % for repo in list(w.user.all_repos):
65 |
66 | ${repo.user.github_username}/${repo.name}
67 | ${repo.description}
68 | ${repo.language}
69 | % if w.show_buttons and w.user.oauth_access_token:
70 | ${w.make_button(repo) | n}
71 | % endif
72 |
73 | % endfor
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/github2fedmsg/models/jsonifiable.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import datetime
18 | import time
19 |
20 | from sqlalchemy.orm import (
21 | class_mapper,
22 | )
23 | from sqlalchemy.orm.properties import RelationshipProperty
24 |
25 |
26 | class JSONifiable(object):
27 | """ A mixin for sqlalchemy models providing a .__json__ method. """
28 |
29 | def __json__(self, seen=None):
30 | """ Returns a dict representation of the object.
31 |
32 | Recursively evaluates .__json__() on its relationships.
33 | """
34 |
35 | if not seen:
36 | seen = []
37 |
38 | properties = list(class_mapper(type(self)).iterate_properties)
39 | relationships = [
40 | p.key for p in properties if type(p) is RelationshipProperty
41 | ]
42 | attrs = [
43 | p.key for p in properties if p.key not in relationships
44 | ]
45 |
46 | d = dict([(attr, getattr(self, attr)) for attr in attrs])
47 |
48 | for attr in relationships:
49 | d[attr] = self._expand(getattr(self, attr), seen)
50 |
51 | if hasattr(self, 'avatar'):
52 | d['avatar'] = self.avatar
53 |
54 | # Serialize datetime objects
55 | for k, v in d.items():
56 | if isinstance(v, datetime.datetime):
57 | d[k] = time.mktime(v.timetuple())
58 |
59 | return d
60 |
61 | def _expand(self, relation, seen):
62 | """ Return the __json__() or id of a sqlalchemy relationship. """
63 |
64 | if hasattr(relation, 'all'):
65 | relation = relation.all()
66 |
67 | if hasattr(relation, '__iter__'):
68 | return [self._expand(item, seen) for item in relation]
69 |
70 | if not relation:
71 | return []
72 |
73 | if type(relation) not in seen:
74 | return relation.__json__(seen + [type(self)])
75 | else:
76 | return self._primary(relation)
77 |
78 | def _primary(self, obj):
79 | """ This is an ugly hack, and can be done much more nicely by
80 | introspecting primary keys...
81 | """
82 |
83 | if hasattr(obj, 'id'):
84 | return obj.id
85 | else:
86 | return obj.username
87 |
--------------------------------------------------------------------------------
/github2fedmsg/githubutils.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | """ Tools for querying github.
18 |
19 | I tried using pygithub3, but it really sucks.
20 | """
21 |
22 | import requests
23 |
24 |
25 | def _link_field_to_dict(field):
26 | """ Utility for ripping apart github's Link header field.
27 | It's kind of ugly.
28 | """
29 |
30 | if not field:
31 | return dict()
32 |
33 | return dict([
34 | (
35 | part.split('; ')[1][5:-1],
36 | part.split('; ')[0][1:-1],
37 | ) for part in field.split(', ')
38 | ])
39 |
40 |
41 | def get_repos(username, auth):
42 | """ username should be a string
43 | auth should be a tuple of username and password.
44 |
45 | item can be one of "repos" or "orgs"
46 | """
47 |
48 | tmpl = "https://api.github.com/users/{username}/repos?per_page=100"
49 | url = tmpl.format(username=username)
50 | return _getter(url, auth)
51 |
52 | def get_orgs(username, auth):
53 | tmpl = "https://api.github.com/users/{username}/orgs?per_page=100"
54 | url = tmpl.format(username=username)
55 | return _getter(url, auth)
56 |
57 |
58 | def _getter(url, auth):
59 | """ Pagination utility. Obnoxious. """
60 |
61 | results = []
62 | link = dict(next=url)
63 | while 'next' in link:
64 |
65 | if isinstance(auth, tuple):
66 | # Is it a (username, password) tuple?
67 | kwargs = dict(auth=auth)
68 | elif isinstance(auth, dict):
69 | # Or, is it an oauth bearer token?
70 | kwargs = dict(params=auth)
71 | else:
72 | raise TypeError("No clue how to handle github auth obj: %r" % auth)
73 |
74 | response = requests.get(link['next'], **kwargs)
75 |
76 | # And.. if we didn't get good results, just bail.
77 | if response.status_code != 200:
78 | raise IOError(
79 | "Non-200 status code %r; %r; %r" % (
80 | response.status_code, url, response.json))
81 |
82 | if callable(response.json):
83 | # Newer python-requests
84 | results += response.json()
85 | else:
86 | # Older python-requests
87 | results += response.json
88 |
89 | link = _link_field_to_dict(response.headers.get('link', None))
90 |
91 | return results
92 |
93 | if __name__ == '__main__':
94 | # Little test.
95 | import getpass
96 | username = raw_input("GitHub Username: ")
97 | password = getpass.getpass()
98 |
99 | results = get_repos(username, (username, password))
100 | print len(results), "repos found."
101 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger.css:
--------------------------------------------------------------------------------
1 | /* line 4, ../../src/sass/messenger.sass */
2 | ul.messenger {
3 | margin: 0;
4 | padding: 0;
5 | }
6 | /* line 8, ../../src/sass/messenger.sass */
7 | ul.messenger > li {
8 | list-style: none;
9 | margin: 0;
10 | padding: 0;
11 | }
12 | /* line 14, ../../src/sass/messenger.sass */
13 | ul.messenger.messenger-empty {
14 | display: none;
15 | }
16 | /* line 17, ../../src/sass/messenger.sass */
17 | ul.messenger .messenger-message {
18 | overflow: hidden;
19 | *zoom: 1;
20 | }
21 | /* line 20, ../../src/sass/messenger.sass */
22 | ul.messenger .messenger-message.messenger-hidden {
23 | display: none;
24 | }
25 | /* line 23, ../../src/sass/messenger.sass */
26 | ul.messenger .messenger-message .messenger-phrase, ul.messenger .messenger-message .messenger-actions a {
27 | padding-right: 5px;
28 | }
29 | /* line 26, ../../src/sass/messenger.sass */
30 | ul.messenger .messenger-message .messenger-actions {
31 | float: right;
32 | }
33 | /* line 29, ../../src/sass/messenger.sass */
34 | ul.messenger .messenger-message .messenger-actions a {
35 | cursor: pointer;
36 | text-decoration: underline;
37 | }
38 | /* line 33, ../../src/sass/messenger.sass */
39 | ul.messenger .messenger-message ul, ul.messenger .messenger-message ol {
40 | margin: 10px 18px 0;
41 | }
42 | /* line 36, ../../src/sass/messenger.sass */
43 | ul.messenger.messenger-fixed {
44 | position: fixed;
45 | z-index: 10000;
46 | }
47 | /* line 40, ../../src/sass/messenger.sass */
48 | ul.messenger.messenger-fixed .messenger-message {
49 | min-width: 0;
50 | -webkit-box-sizing: border-box;
51 | -moz-box-sizing: border-box;
52 | box-sizing: border-box;
53 | }
54 | /* line 45, ../../src/sass/messenger.sass */
55 | ul.messenger.messenger-fixed .message .messenger-actions {
56 | float: left;
57 | }
58 | /* line 48, ../../src/sass/messenger.sass */
59 | ul.messenger.messenger-fixed.messenger-on-top {
60 | top: 20px;
61 | }
62 | /* line 51, ../../src/sass/messenger.sass */
63 | ul.messenger.messenger-fixed.messenger-on-bottom {
64 | bottom: 20px;
65 | }
66 | /* line 54, ../../src/sass/messenger.sass */
67 | ul.messenger.messenger-fixed.messenger-on-top, ul.messenger.messenger-fixed.messenger-on-bottom {
68 | left: 50%;
69 | width: 800px;
70 | margin-left: -400px;
71 | }
72 | @media (max-width: 960px) {
73 | /* line 54, ../../src/sass/messenger.sass */
74 | ul.messenger.messenger-fixed.messenger-on-top, ul.messenger.messenger-fixed.messenger-on-bottom {
75 | left: 10%;
76 | width: 80%;
77 | margin-left: 0px;
78 | }
79 | }
80 | /* line 64, ../../src/sass/messenger.sass */
81 | ul.messenger.messenger-fixed.messenger-on-top.messenger-on-right, ul.messenger.messenger-fixed.messenger-on-bottom.messenger-on-right {
82 | right: 20px;
83 | left: auto;
84 | }
85 | /* line 68, ../../src/sass/messenger.sass */
86 | ul.messenger.messenger-fixed.messenger-on-top.messenger-on-left, ul.messenger.messenger-fixed.messenger-on-bottom.messenger-on-left {
87 | left: 20px;
88 | margin-left: 0px;
89 | }
90 | /* line 72, ../../src/sass/messenger.sass */
91 | ul.messenger.messenger-fixed.messenger-on-right, ul.messenger.messenger-fixed.messenger-on-left {
92 | width: 350px;
93 | }
94 | /* line 75, ../../src/sass/messenger.sass */
95 | ul.messenger.messenger-fixed.messenger-on-right .messenger-actions, ul.messenger.messenger-fixed.messenger-on-left .messenger-actions {
96 | float: left;
97 | }
98 | /* line 78, ../../src/sass/messenger.sass */
99 | ul.messenger .messenger-spinner {
100 | display: none;
101 | }
102 |
--------------------------------------------------------------------------------
/github2fedmsg/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from pyramid.config import Configurator
18 | from pyramid.session import UnencryptedCookieSessionFactoryConfig
19 | from pyramid.authentication import AuthTktAuthenticationPolicy
20 | #from pyramid.authorization import ACLAuthorizationPolicy
21 | from pyramid.security import authenticated_userid
22 | from sqlalchemy import engine_from_config
23 |
24 | import pyramid_mako
25 |
26 | import ConfigParser
27 | import os
28 |
29 | import github2fedmsg.models
30 | import github2fedmsg.traversal
31 | import github2fedmsg.custom_openid
32 |
33 |
34 | def get_user(request):
35 | """ A utility property hanging on 'request' """
36 | username = authenticated_userid(request)
37 | query = github2fedmsg.models.User.query.filter_by(username=username)
38 | if username and query.count() > 0:
39 | return query.one()
40 |
41 |
42 | def main(global_config, **settings):
43 | """ This function returns a Pyramid WSGI application.
44 | """
45 |
46 | # Load secret stuff from secret.ini.
47 | try:
48 | default_path = os.path.abspath("secret.ini")
49 | secret_path = settings.get('secret_config_path', default_path)
50 | # TODO: There is a better way to log this message than print.
51 | print "Reading secrets from %r" % secret_path
52 | parser = ConfigParser.ConfigParser()
53 | parser.read(secret_path)
54 | secret_config = dict(parser.items("github2fedmsg"))
55 | settings.update(secret_config)
56 | except Exception as e:
57 | # TODO: There is a better way to log this message than print.
58 | print 'Failed to load secret.ini. Reason: %r' % str(e)
59 |
60 | crappy_session_factory = UnencryptedCookieSessionFactoryConfig(settings['session.secret'])
61 | authn_policy = AuthTktAuthenticationPolicy(secret=settings['authnsecret'], hashalg='sha256')
62 |
63 | engine = engine_from_config(settings, 'sqlalchemy.')
64 | github2fedmsg.models.DBSession.configure(bind=engine)
65 |
66 | config = Configurator(
67 | settings=settings,
68 | root_factory=github2fedmsg.traversal.make_root,
69 | session_factory=crappy_session_factory,
70 | authentication_policy=authn_policy,
71 | #authorization_policy=authz_policy,
72 | )
73 |
74 | # Make it so we can do "request.user" in templates.
75 | if hasattr(config, "add_request_method"):
76 | # Pyramid >= 1.4
77 | config.add_request_method(get_user, 'user', reify=True)
78 | else:
79 | config.set_request_property(get_user, 'user', reify=True)
80 |
81 | config.include('pyramid_mako')
82 | config.add_mako_renderer('.mak')
83 |
84 | config.include('velruse.providers.github')
85 | config.add_github_login_from_settings()
86 |
87 | config.include('velruse.providers.openid')
88 | github2fedmsg.custom_openid.add_openid_login(
89 | config,
90 | realm=settings.get('velruse.openid.realm'),
91 | identity_provider=settings.get('velruse.openid.identifier'),
92 | )
93 |
94 | config.add_static_view('static', 'static', cache_max_age=3600)
95 | config.add_route('home', '/')
96 | config.add_route('logout', '/logout')
97 | config.add_route('webhook', '/webhook')
98 | config.add_route('forget_github_token', '/forget_github_token')
99 | config.scan()
100 | return config.make_wsgi_app()
101 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger-theme-block.css:
--------------------------------------------------------------------------------
1 | /* line 4, ../../src/sass/messenger-theme-block.sass */
2 | ul.messenger.messenger-theme-block.messenger-fixed {
3 | width: 100%;
4 | top: 0;
5 | left: 0;
6 | margin-left: 0px;
7 | }
8 | /* line 10, ../../src/sass/messenger-theme-block.sass */
9 | ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-bottom {
10 | top: auto;
11 | bottom: 0;
12 | }
13 | /* line 14, ../../src/sass/messenger-theme-block.sass */
14 | ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-top {
15 | top: 0px;
16 | bottom: auto;
17 | }
18 | /* line 18, ../../src/sass/messenger-theme-block.sass */
19 | ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-top, ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-bottom {
20 | left: 0px;
21 | right: 0px;
22 | }
23 | /* line 22, ../../src/sass/messenger-theme-block.sass */
24 | ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-top.messenger-on-right, ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-top.messenger-on-left, ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-bottom.messenger-on-right, ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-bottom.messenger-on-left {
25 | width: 350px;
26 | }
27 | /* line 25, ../../src/sass/messenger-theme-block.sass */
28 | ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-top.messenger-on-left, ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-bottom.messenger-on-left {
29 | right: auto;
30 | }
31 | /* line 28, ../../src/sass/messenger-theme-block.sass */
32 | ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-top.messenger-on-right, ul.messenger.messenger-theme-block.messenger-fixed.messenger-on-bottom.messenger-on-right {
33 | left: auto;
34 | }
35 | /* line 31, ../../src/sass/messenger-theme-block.sass */
36 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message-slot {
37 | max-width: none;
38 | }
39 | /* line 34, ../../src/sass/messenger-theme-block.sass */
40 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message {
41 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
42 | border-width: 1px;
43 | border-style: solid;
44 | text-align: center;
45 | padding: 1em;
46 | }
47 | /* line 41, ../../src/sass/messenger-theme-block.sass */
48 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message.alert-warning {
49 | color: #c09853;
50 | background-color: #fcf8e3;
51 | border-color: #fbeed5;
52 | }
53 | /* line 46, ../../src/sass/messenger-theme-block.sass */
54 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message.alert-error, ul.messenger.messenger-theme-block.messenger-fixed .messenger-message.alert-danger {
55 | color: #b94a48;
56 | background-color: #f2dede;
57 | border-color: #eed3d7;
58 | }
59 | /* line 51, ../../src/sass/messenger-theme-block.sass */
60 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message.alert-success {
61 | color: #468847;
62 | background-color: #dff0d8;
63 | border-color: #d6e9c6;
64 | }
65 | /* line 56, ../../src/sass/messenger-theme-block.sass */
66 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message.alert-info {
67 | color: #3a87ad;
68 | background-color: #d9edf7;
69 | border-color: #bce8f1;
70 | }
71 | /* line 61, ../../src/sass/messenger-theme-block.sass */
72 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message .messenger-close {
73 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
74 | opacity: 0.5;
75 | padding: 0;
76 | cursor: pointer;
77 | color: inherit;
78 | background: transparent;
79 | border: 0;
80 | -webkit-appearance: none;
81 | float: right;
82 | position: relative;
83 | top: -13px;
84 | left: 11px;
85 | font-size: 22px;
86 | }
87 | /* line 75, ../../src/sass/messenger-theme-block.sass */
88 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message .messenger-close:hover {
89 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
90 | opacity: 0.8;
91 | }
92 | /* line 78, ../../src/sass/messenger-theme-block.sass */
93 | ul.messenger.messenger-theme-block.messenger-fixed .messenger-message .messenger-close:active {
94 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
95 | opacity: 1;
96 | }
97 |
--------------------------------------------------------------------------------
/github2fedmsg/views/auth.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from pyramid.view import view_config
18 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden
19 | from pyramid.security import (
20 | authenticated_userid,
21 | remember,
22 | forget,
23 | )
24 |
25 | import github2fedmsg.models as m
26 |
27 |
28 | @view_config(context='velruse.AuthenticationComplete')
29 | def login_complete_view(request):
30 | """ This handles *both* login with FAS and login with github.. """
31 |
32 | # Github gives us:
33 | #{'accounts': [{'domain': 'github.com',
34 | # 'userid': 331338,
35 | # 'username': u'ralphbean'}],
36 | # 'displayName': u'Ralph Bean',
37 | # 'emails': [{'value': u'rbean@redhat.com'}],
38 | # 'preferredUsername': u'ralphbean'}
39 |
40 | # FAS gives us:
41 | #{'accounts': [{'domain': 'openid.net',
42 | # 'username': 'http://ralph.id.fedoraproject.org/'}],
43 | # 'displayName': u'Ralph Bean',
44 | # 'emails': [u'rbean@redhat.com'],
45 | # 'name': {'formatted': u'Ralph Bean'},
46 | # 'preferredUsername': u'ralph'}
47 |
48 | ctx = request.context
49 | accounts = ctx.profile['accounts']
50 | home = request.route_url('home')
51 |
52 | if accounts and accounts[0]['domain'] == 'github.com':
53 | if not request.user:
54 | # Bad scene. Someone is trying to link with github.com while not
55 | # yet being signed in through FAS. We will just deny this.
56 | return HTTPForbidden()
57 | token = ctx.credentials['oauthAccessToken']
58 | request.user.github_username = ctx.profile['preferredUsername']
59 | request.user.oauth_access_token = token
60 | request.session['token'] = token
61 | return HTTPFound(location=home + request.user.username)
62 |
63 | try:
64 | username = ctx.profile['preferredUsername']
65 | except KeyError:
66 | username = accounts[0]["username"].split("/")[-2]
67 | full_name = ctx.profile.get('displayName', username)
68 | emails = ctx.profile.get('emails', [])
69 |
70 | if emails:
71 | if isinstance(emails[0], dict):
72 | emails = [item['value'] for item in emails if item['value']]
73 |
74 | emails = ','.join(emails)
75 |
76 | query = m.User.query.filter_by(username=username)
77 | if query.count() == 0:
78 | m.DBSession.add(m.User(
79 | username=username,
80 | full_name=full_name,
81 | emails=emails,
82 | ))
83 |
84 | user = query.one()
85 | headers = remember(request, username)
86 | request.session['token'] = user.oauth_access_token
87 | return HTTPFound(location=home + user.username, headers=headers)
88 |
89 |
90 | @view_config(context='velruse.AuthenticationDenied', renderer='json')
91 | def login_denied_view(request):
92 | # TODO -- fancy flash and redirect
93 | return {'result': 'denied'}
94 |
95 |
96 | @view_config(route_name='logout')
97 | def logout(request):
98 | headers = forget(request)
99 | home = request.route_url('home')
100 | return HTTPFound(location=home, headers=headers)
101 |
102 |
103 | @view_config(route_name='forget_github_token')
104 | def forget_github_token(request):
105 | if 'token' in request.session:
106 | request.session['token']
107 |
108 | username = request.user.username
109 | home = request.route_url('home')
110 |
111 | import transaction
112 | #request.user.github_username = None
113 | request.user.oauth_access_token = None
114 | transaction.commit()
115 |
116 | return HTTPFound(location=home + username)
117 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger-theme-ice.css:
--------------------------------------------------------------------------------
1 | @import url("//fonts.googleapis.com/css?family=Raleway:400");
2 | /* line 12, ../../src/sass/messenger-theme-ice.sass */
3 | ul.messenger-theme-ice {
4 | -moz-user-select: none;
5 | -webkit-user-select: none;
6 | -o-user-select: none;
7 | user-select: none;
8 | font-family: "Raleway", sans-serif;
9 | }
10 | /* line 16, ../../src/sass/messenger-theme-ice.sass */
11 | ul.messenger-theme-ice .messenger-message {
12 | -webkit-border-radius: 5px;
13 | -moz-border-radius: 5px;
14 | -ms-border-radius: 5px;
15 | -o-border-radius: 5px;
16 | border-radius: 5px;
17 | -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.14), 0 4px #aaaaaa, 0 5px rgba(0, 0, 0, 0.05);
18 | -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.14), 0 4px #aaaaaa, 0 5px rgba(0, 0, 0, 0.05);
19 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.14), 0 4px #aaaaaa, 0 5px rgba(0, 0, 0, 0.05);
20 | border: 0px;
21 | background-color: #f6f6f6;
22 | position: relative;
23 | margin-bottom: 1.5em;
24 | font-size: 13px;
25 | color: #666666;
26 | font-weight: 500;
27 | padding: 12px 22px;
28 | }
29 | /* line 28, ../../src/sass/messenger-theme-ice.sass */
30 | ul.messenger-theme-ice .messenger-message .messenger-close {
31 | position: absolute;
32 | top: 0px;
33 | right: 0px;
34 | color: #888888;
35 | opacity: 1;
36 | font-weight: bold;
37 | display: block;
38 | font-size: 20px;
39 | line-height: 20px;
40 | padding: 8px 10px 7px 7px;
41 | cursor: pointer;
42 | background: transparent;
43 | border: 0;
44 | -webkit-appearance: none;
45 | }
46 | /* line 44, ../../src/sass/messenger-theme-ice.sass */
47 | ul.messenger-theme-ice .messenger-message .messenger-close:hover {
48 | color: #444444;
49 | }
50 | /* line 47, ../../src/sass/messenger-theme-ice.sass */
51 | ul.messenger-theme-ice .messenger-message .messenger-close:active {
52 | color: #222222;
53 | }
54 | /* line 50, ../../src/sass/messenger-theme-ice.sass */
55 | ul.messenger-theme-ice .messenger-message .messenger-actions {
56 | float: none;
57 | margin-top: 10px;
58 | }
59 | /* line 54, ../../src/sass/messenger-theme-ice.sass */
60 | ul.messenger-theme-ice .messenger-message .messenger-actions a {
61 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.05);
62 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.05);
63 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.05);
64 | -webkit-border-radius: 4px;
65 | -moz-border-radius: 4px;
66 | -ms-border-radius: 4px;
67 | -o-border-radius: 4px;
68 | border-radius: 4px;
69 | position: relative;
70 | text-decoration: none;
71 | display: inline-block;
72 | padding: 10px;
73 | color: #888888;
74 | margin-right: 10px;
75 | padding: 3px 10px 5px;
76 | text-transform: capitalize;
77 | }
78 | /* line 66, ../../src/sass/messenger-theme-ice.sass */
79 | ul.messenger-theme-ice .messenger-message .messenger-actions a:hover, ul.messenger-theme-ice .messenger-message .messenger-actions a:active {
80 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15), 0 2px #aaaaaa;
81 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15), 0 2px #aaaaaa;
82 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15), 0 2px #aaaaaa;
83 | color: #444444;
84 | }
85 | /* line 70, ../../src/sass/messenger-theme-ice.sass */
86 | ul.messenger-theme-ice .messenger-message .messenger-actions a:active {
87 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15), 0 1px #aaaaaa;
88 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15), 0 1px #aaaaaa;
89 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15), 0 1px #aaaaaa;
90 | top: 1px;
91 | }
92 | /* line 74, ../../src/sass/messenger-theme-ice.sass */
93 | ul.messenger-theme-ice .messenger-message .messenger-actions .messenger-phrase {
94 | display: none;
95 | }
96 | /* line 77, ../../src/sass/messenger-theme-ice.sass */
97 | ul.messenger-theme-ice .messenger-message .messenger-message-inner:before {
98 | display: block;
99 | z-index: 20;
100 | font-weight: bold;
101 | margin-bottom: 2px;
102 | }
103 | /* line 84, ../../src/sass/messenger-theme-ice.sass */
104 | ul.messenger-theme-ice .messenger-message.alert-success .messenger-message-inner:before {
105 | content: "Success";
106 | }
107 | /* line 88, ../../src/sass/messenger-theme-ice.sass */
108 | ul.messenger-theme-ice .messenger-message.alert-error .messenger-message-inner:before {
109 | content: "Error";
110 | }
111 | /* line 92, ../../src/sass/messenger-theme-ice.sass */
112 | ul.messenger-theme-ice .messenger-message.alert-info .messenger-message-inner:before {
113 | content: "Information";
114 | }
115 |
--------------------------------------------------------------------------------
/tools/fake-github-post.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | import json
18 | import hmac
19 | import hashlib
20 | import urllib
21 | import requests
22 |
23 | payload = {
24 | "after": "3ef21292dd6448d4731f49864844d458ad3801a5",
25 | "before": "541d26405960ef934a398d7ee413bf7855cbe220",
26 | "commits": [
27 | { "added": [],
28 | "author": { "email": "rbean@redhat.com",
29 | "name": "Ralph Bean",
30 | "username": "ralphbean" },
31 | "committer": { "email": "rbean@redhat.com",
32 | "name": "Ralph Bean",
33 | "username": "ralphbean" },
34 | "distinct": True,
35 | "id": "81eacd09c4d6526bea1d14e3fbc17ef0fd1d9192",
36 | "message": "Authentication policy.",
37 | "modified": [ "statatat/__init__.py",
38 | "statatat/views.py" ],
39 | "removed": [],
40 | "timestamp": "2012-08-25T06:30:21-07:00",
41 | "url": "https://github.com/ralphbean/statatat/commit/81eacd09c4d6526bea1d14e3fbc17ef0fd1d9192" },
42 | { "added": [],
43 | "author": { "email": "rbean@redhat.com",
44 | "name": "Ralph Bean",
45 | "username": "ralphbean" },
46 | "committer": { "email": "rbean@redhat.com",
47 | "name": "Ralph Bean",
48 | "username": "ralphbean" },
49 | "distinct": True,
50 | "id": "ffedad58abec3cbf6546daedfb4249db5b994203",
51 | "message": "Use BeforeRender like a pro.",
52 | "modified": [ "statatat/views.py" ],
53 | "removed": [],
54 | "timestamp": "2012-08-25T06:36:24-07:00",
55 | "url": "https://github.com/ralphbean/statatat/commit/ffedad58abec3cbf6546daedfb4249db5b994203" },
56 | { "added": [],
57 | "author": { "email": "rbean@redhat.com",
58 | "name": "Ralph Bean",
59 | "username": "ralphbean" },
60 | "committer": { "email": "rbean@redhat.com",
61 | "name": "Ralph Bean",
62 | "username": "ralphbean" },
63 | "distinct": True,
64 | "id": "3ef21292dd6448d4731f49864844d458ad3801a5",
65 | "message": "Keep \"identity\" in the globals like TG2.",
66 | "modified": [ "statatat/views.py" ],
67 | "removed": [],
68 | "timestamp": "2012-08-25T06:37:21-07:00",
69 | "url": "https://github.com/ralphbean/statatat/commit/3ef21292dd6448d4731f49864844d458ad3801a5"
70 | }
71 | ],
72 | "compare": "https://github.com/ralphbean/statatat/compare/541d26405960...3ef21292dd64",
73 | "created": False,
74 | "deleted": False,
75 | "forced": False,
76 | "head_commit": { "added": [],
77 | "author": { "email": "rbean@redhat.com",
78 | "name": "Ralph Bean",
79 | "username": "ralphbean" },
80 | "committer": { "email": "rbean@redhat.com",
81 | "name": "Ralph Bean",
82 | "username": "ralphbean" },
83 | "distinct": True,
84 | "id": "3ef21292dd6448d4731f49864844d458ad3801a5",
85 | "message": "Keep \"identity\" in the globals like TG2.",
86 | "modified": [ "statatat/views.py" ],
87 | "removed": [],
88 | "timestamp": "2012-08-25T06:37:21-07:00",
89 | "url": "https://github.com/ralphbean/statatat/commit/3ef21292dd6448d4731f49864844d458ad3801a5" },
90 | "pusher": { "name": "none" },
91 | "ref": "refs/heads/master",
92 | "repository": { "created_at": "2012-07-05T21:22:12-07:00",
93 | "description": "Embeddable realtime dev widgets.",
94 | "fork": False,
95 | "forks": 0,
96 | "has_downloads": True,
97 | "has_issues": True,
98 | "has_wiki": False,
99 | "language": "Python",
100 | "master_branch": "develop",
101 | "name": "statatat",
102 | "open_issues": 3,
103 | "owner": { "email": "ralph.bean@gmail.com",
104 | "name": "ralphbean" },
105 | "private": False,
106 | "pushed_at": "2012-09-16T20:03:33-07:00",
107 | "size": 272,
108 | "stargazers": 3,
109 | "url": "https://github.com/ralphbean/statatat",
110 | "watchers": 3 } }
111 |
112 | blob = {'payload': json.dumps(payload)}
113 |
114 | github_secret = 'changeme!'
115 | body = urllib.urlencode(blob)
116 | hex = hmac.new(github_secret, body, hashlib.sha1).hexdigest()
117 | headers = {
118 | 'X-Hub-Signature': 'sha1=%s' % hex,
119 | 'X-GitHub-Event': 'this is going to be something...',
120 | }
121 |
122 | response = requests.post(
123 | "http://localhost:6543/webhook",
124 | data=blob,
125 | headers=headers,
126 | )
127 |
128 | print response.status_code
129 | print response.text
130 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger-spinner.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes ui-spinner-rotate-right {
2 | /* line 64, ../../src/sass/messenger-spinner.scss */
3 | 0% {
4 | -webkit-transform: rotate(0deg);
5 | }
6 |
7 | /* line 65, ../../src/sass/messenger-spinner.scss */
8 | 25% {
9 | -webkit-transform: rotate(180deg);
10 | }
11 |
12 | /* line 66, ../../src/sass/messenger-spinner.scss */
13 | 50% {
14 | -webkit-transform: rotate(180deg);
15 | }
16 |
17 | /* line 67, ../../src/sass/messenger-spinner.scss */
18 | 75% {
19 | -webkit-transform: rotate(360deg);
20 | }
21 |
22 | /* line 68, ../../src/sass/messenger-spinner.scss */
23 | 100% {
24 | -webkit-transform: rotate(360deg);
25 | }
26 | }
27 |
28 | @-webkit-keyframes ui-spinner-rotate-left {
29 | /* line 72, ../../src/sass/messenger-spinner.scss */
30 | 0% {
31 | -webkit-transform: rotate(0deg);
32 | }
33 |
34 | /* line 73, ../../src/sass/messenger-spinner.scss */
35 | 25% {
36 | -webkit-transform: rotate(0deg);
37 | }
38 |
39 | /* line 74, ../../src/sass/messenger-spinner.scss */
40 | 50% {
41 | -webkit-transform: rotate(180deg);
42 | }
43 |
44 | /* line 75, ../../src/sass/messenger-spinner.scss */
45 | 75% {
46 | -webkit-transform: rotate(180deg);
47 | }
48 |
49 | /* line 76, ../../src/sass/messenger-spinner.scss */
50 | 100% {
51 | -webkit-transform: rotate(360deg);
52 | }
53 | }
54 |
55 | @-moz-keyframes ui-spinner-rotate-right {
56 | /* line 80, ../../src/sass/messenger-spinner.scss */
57 | 0% {
58 | -moz-transform: rotate(0deg);
59 | }
60 |
61 | /* line 81, ../../src/sass/messenger-spinner.scss */
62 | 25% {
63 | -moz-transform: rotate(180deg);
64 | }
65 |
66 | /* line 82, ../../src/sass/messenger-spinner.scss */
67 | 50% {
68 | -moz-transform: rotate(180deg);
69 | }
70 |
71 | /* line 83, ../../src/sass/messenger-spinner.scss */
72 | 75% {
73 | -moz-transform: rotate(360deg);
74 | }
75 |
76 | /* line 84, ../../src/sass/messenger-spinner.scss */
77 | 100% {
78 | -moz-transform: rotate(360deg);
79 | }
80 | }
81 |
82 | @-moz-keyframes ui-spinner-rotate-left {
83 | /* line 88, ../../src/sass/messenger-spinner.scss */
84 | 0% {
85 | -moz-transform: rotate(0deg);
86 | }
87 |
88 | /* line 89, ../../src/sass/messenger-spinner.scss */
89 | 25% {
90 | -moz-transform: rotate(0deg);
91 | }
92 |
93 | /* line 90, ../../src/sass/messenger-spinner.scss */
94 | 50% {
95 | -moz-transform: rotate(180deg);
96 | }
97 |
98 | /* line 91, ../../src/sass/messenger-spinner.scss */
99 | 75% {
100 | -moz-transform: rotate(180deg);
101 | }
102 |
103 | /* line 92, ../../src/sass/messenger-spinner.scss */
104 | 100% {
105 | -moz-transform: rotate(360deg);
106 | }
107 | }
108 |
109 | @keyframes ui-spinner-rotate-right {
110 | /* line 96, ../../src/sass/messenger-spinner.scss */
111 | 0% {
112 | transform: rotate(0deg);
113 | }
114 |
115 | /* line 97, ../../src/sass/messenger-spinner.scss */
116 | 25% {
117 | transform: rotate(180deg);
118 | }
119 |
120 | /* line 98, ../../src/sass/messenger-spinner.scss */
121 | 50% {
122 | transform: rotate(180deg);
123 | }
124 |
125 | /* line 99, ../../src/sass/messenger-spinner.scss */
126 | 75% {
127 | transform: rotate(360deg);
128 | }
129 |
130 | /* line 100, ../../src/sass/messenger-spinner.scss */
131 | 100% {
132 | transform: rotate(360deg);
133 | }
134 | }
135 |
136 | @keyframes ui-spinner-rotate-left {
137 | /* line 104, ../../src/sass/messenger-spinner.scss */
138 | 0% {
139 | transform: rotate(0deg);
140 | }
141 |
142 | /* line 105, ../../src/sass/messenger-spinner.scss */
143 | 25% {
144 | transform: rotate(0deg);
145 | }
146 |
147 | /* line 106, ../../src/sass/messenger-spinner.scss */
148 | 50% {
149 | transform: rotate(180deg);
150 | }
151 |
152 | /* line 107, ../../src/sass/messenger-spinner.scss */
153 | 75% {
154 | transform: rotate(180deg);
155 | }
156 |
157 | /* line 108, ../../src/sass/messenger-spinner.scss */
158 | 100% {
159 | transform: rotate(360deg);
160 | }
161 | }
162 |
163 | /* line 116, ../../src/sass/messenger-spinner.scss */
164 | .messenger-spinner {
165 | position: relative;
166 | border-radius: 100%;
167 | }
168 | /* line 120, ../../src/sass/messenger-spinner.scss */
169 | ul.messenger.messenger-spinner-active .messenger-spinner .messenger-spinner {
170 | display: block;
171 | }
172 | /* line 124, ../../src/sass/messenger-spinner.scss */
173 | .messenger-spinner .messenger-spinner-side {
174 | width: 50%;
175 | height: 100%;
176 | overflow: hidden;
177 | position: absolute;
178 | }
179 | /* line 130, ../../src/sass/messenger-spinner.scss */
180 | .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
181 | border-radius: 999px;
182 | position: absolute;
183 | width: 100%;
184 | height: 100%;
185 | -webkit-animation-iteration-count: infinite;
186 | -moz-animation-iteration-count: infinite;
187 | -ms-animation-iteration-count: infinite;
188 | -o-animation-iteration-count: infinite;
189 | animation-iteration-count: infinite;
190 | -webkit-animation-timing-function: linear;
191 | -moz-animation-timing-function: linear;
192 | -ms-animation-timing-function: linear;
193 | -o-animation-timing-function: linear;
194 | animation-timing-function: linear;
195 | }
196 | /* line 140, ../../src/sass/messenger-spinner.scss */
197 | .messenger-spinner .messenger-spinner-side-left {
198 | left: 0;
199 | }
200 | /* line 143, ../../src/sass/messenger-spinner.scss */
201 | .messenger-spinner .messenger-spinner-side-left .messenger-spinner-fill {
202 | left: 100%;
203 | border-top-left-radius: 0;
204 | border-bottom-left-radius: 0;
205 | -webkit-animation-name: ui-spinner-rotate-left;
206 | -moz-animation-name: ui-spinner-rotate-left;
207 | -ms-animation-name: ui-spinner-rotate-left;
208 | -o-animation-name: ui-spinner-rotate-left;
209 | animation-name: ui-spinner-rotate-left;
210 | -webkit-transform-origin: 0 50%;
211 | -moz-transform-origin: 0 50%;
212 | -ms-transform-origin: 0 50%;
213 | -o-transform-origin: 0 50%;
214 | transform-origin: 0 50%;
215 | }
216 | /* line 152, ../../src/sass/messenger-spinner.scss */
217 | .messenger-spinner .messenger-spinner-side-right {
218 | left: 50%;
219 | }
220 | /* line 155, ../../src/sass/messenger-spinner.scss */
221 | .messenger-spinner .messenger-spinner-side-right .messenger-spinner-fill {
222 | left: -100%;
223 | border-top-right-radius: 0;
224 | border-bottom-right-radius: 0;
225 | -webkit-animation-name: ui-spinner-rotate-right;
226 | -moz-animation-name: ui-spinner-rotate-right;
227 | -ms-animation-name: ui-spinner-rotate-right;
228 | -o-animation-name: ui-spinner-rotate-right;
229 | animation-name: ui-spinner-rotate-right;
230 | -webkit-transform-origin: 100% 50%;
231 | -moz-transform-origin: 100% 50%;
232 | -ms-transform-origin: 100% 50%;
233 | -o-transform-origin: 100% 50%;
234 | transform-origin: 100% 50%;
235 | }
236 |
--------------------------------------------------------------------------------
/github2fedmsg/models/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from sqlalchemy import (
18 | Table,
19 | Column,
20 | Integer,
21 | DateTime,
22 | Boolean,
23 | Unicode,
24 | ForeignKey,
25 | and_,
26 | )
27 |
28 | from sqlalchemy.ext.declarative import declarative_base
29 |
30 | from sqlalchemy.orm import (
31 | scoped_session,
32 | sessionmaker,
33 | relation,
34 | backref,
35 | )
36 |
37 | import pyramid.threadlocal
38 | import github2fedmsg.traversal
39 | import datetime
40 | from hashlib import sha256
41 | from .jsonifiable import JSONifiable
42 |
43 | from zope.sqlalchemy import ZopeTransactionExtension
44 |
45 | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
46 | Base = declarative_base(cls=JSONifiable)
47 | Base.query = DBSession.query_property()
48 |
49 | import github2fedmsg.githubutils as gh
50 |
51 | org_to_user_mapping = Table(
52 | 'org_to_user_mapping', Base.metadata,
53 | Column('org_id', Unicode, ForeignKey('users.github_username'), primary_key=True),
54 | Column('usr_id', Unicode, ForeignKey('users.github_username'), primary_key=True),
55 | )
56 |
57 | import logging
58 | log = logging.getLogger("github2fedmsg.models")
59 |
60 |
61 | class User(Base):
62 | __tablename__ = 'users'
63 | username = Column(Unicode, primary_key=True)
64 | github_username = Column(Unicode, unique=True)
65 | emails = Column(Unicode, nullable=False)
66 | full_name = Column(Unicode, nullable=False)
67 | oauth_access_token = Column(Unicode)
68 | created_on = Column(DateTime, default=datetime.datetime.now)
69 | repos = relation('Repo', backref=('user'))
70 |
71 | @property
72 | def openid_url(self):
73 | return "http://%s.id.fedoraproject.org/" % self.username
74 |
75 | @property
76 | def all_repos(self):
77 | return sum(
78 | [self.repos] + [org.repos for org in self.organizations],
79 | [])
80 |
81 | def sync_repos(self, gh_auth):
82 | """ Ask github about what repos I have and cache that. """
83 | gh_repos = gh.get_repos(self.github_username, gh_auth)
84 |
85 | # TODO -- fix this. this is inefficient
86 | for repo in gh_repos:
87 |
88 | if Repo.query.filter(and_(
89 | Repo.name==repo['name'],
90 | Repo.username==self.github_username
91 | )).count() < 1:
92 | github2fedmsg.models.DBSession.add(github2fedmsg.models.Repo(
93 | user=self,
94 | name=unicode(repo['name']),
95 | description=unicode(repo['description']),
96 | language=unicode(repo['language']),
97 | ))
98 |
99 | # Refresh my list of organizations.
100 | if not self.users:
101 | # Then I am a real User.
102 | gh_orgs = gh.get_orgs(self.github_username, gh_auth)
103 | for o in gh_orgs:
104 | query = User.query.filter(User.github_username==o['login'])
105 | if query.count() < 1:
106 | log.debug("Adding new org %r" % o['login'])
107 | organization = User(
108 | username="github_org_" + o['login'],
109 | github_username=o['login'],
110 | full_name='',
111 | emails='')
112 | DBSession.add(organization)
113 | else:
114 | log.debug("Found prexisting org %r" % o['login'])
115 | organization = query.one()
116 |
117 | if self not in organization.users:
118 | log.debug("Adding %r to %r" % (
119 | self.github_username, organization.github_username))
120 | organization.users.append(self)
121 | DBSession.flush()
122 | else:
123 | log.debug("Already in %r" % organization.github_username)
124 | else:
125 | # I am an organization. Do not recurse.
126 | pass
127 |
128 | # Follow up with all my orgs too (they are also "User"s)
129 | for organization in self.organizations:
130 | organization.sync_repos(gh_auth)
131 |
132 | @property
133 | def total_enabled_repos(self):
134 | return sum([1 for repo in self.repos if repo.enabled])
135 |
136 | @property
137 | def percent_enabled_repos(self):
138 | # Avoid division by zero
139 | if not len(self.repos):
140 | return 0
141 |
142 | return 100.0 * self.total_enabled_repos / len(self.repos)
143 |
144 | @property
145 | def avatar(self):
146 | digest = sha256(self.openid_url).hexdigest()
147 | return "https://seccdn.libravatar.org/avatar/%s?d=retro" % digest
148 |
149 | @property
150 | def created_on_fmt(self):
151 | return str(self.created_on)
152 |
153 | def __getitem__(self, key):
154 | already_visited = getattr(self, '_visited', False)
155 | if not already_visited and key == self.github_username:
156 | self._visited = True
157 | return self
158 |
159 | for r in self.repos:
160 | if r.name == key:
161 | return r
162 |
163 | for o in self.organizations:
164 | try:
165 | return o[key]
166 | except KeyError:
167 | pass
168 |
169 | raise KeyError(
170 | "No such repo %r associated with %s" % (key, self.username))
171 |
172 | def repo_by_name(self, repo_name):
173 | return self[repo_name]
174 |
175 | User.__mapper__.add_property('organizations', relation(
176 | User,
177 | primaryjoin=User.github_username == org_to_user_mapping.c.org_id,
178 | secondaryjoin=org_to_user_mapping.c.usr_id == User.github_username,
179 | secondary=org_to_user_mapping,
180 | doc="List of this users organizations",
181 | backref=backref('users', doc="List of this organizations users")
182 | ))
183 |
184 |
185 | class Repo(Base):
186 | __tablename__ = 'repos'
187 | id = Column(Integer, primary_key=True)
188 | name = Column(Unicode, nullable=False)
189 | description = Column(Unicode, nullable=False)
190 | language = Column(Unicode, nullable=False)
191 | username = Column(Unicode, ForeignKey('users.github_username'))
192 |
193 | enabled = Column(Boolean, default=False)
194 |
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 |
4 | 0.3.6
5 | -----
6 |
7 | - Convert github.watch messages to github.star messages. `8ead823b6 `_
8 | - Merge pull request #16 from fedora-infra/feature/watch-to-star `288a5b85c `_
9 |
10 | 0.3.5
11 | -----
12 |
13 | - Change in default consumer key. `132d8cbeb `_
14 | - Pull in messenger-1.4.1. `d46bd97bf `_
15 | - Use messenger.js to show errors. `a2029df93 `_
16 | - Typofix `c67f97e0e `_
17 | - Include messenger LICENSE. `cb98f6c7b `_
18 | - Merge pull request #15 from fedora-infra/feature/name-name `18fc7b0c4 `_
19 | - Merge pull request #14 from fedora-infra/feature/error-messages `7d389e0ac `_
20 |
21 | 0.3.4
22 | -----
23 |
24 | - Careful with the repo owner `3c083026a `_
25 | - Merge pull request #11 from fedora-infra/feature/payload-changes `9f2de8581 `_
26 | - Update TODO.md `8a6c50bf4 `_
27 | - Might as well be even more careful here. `1b64c77a5 `_
28 |
29 | 0.3.3
30 | -----
31 |
32 | - A slash makes all the difference. `e56562181 `_
33 | - Preserve target_url `fee1c01ca `_
34 | - Add a ```` tag. `a66d502b3 `_
35 | - Include repo owner in passive actions `ba67db9fa `_
36 | - Merge pull request #8 from fedora-infra/feature/include-owners `2220c655d `_
37 | - Fix an 'unbound session' error. `4d6f4af9d `_
38 | - Merge pull request #9 from fedora-infra/feature/fix-unbound-session-error `163e7afc1 `_
39 | - Mark resources as traversed. `4347c0176 `_
40 | - Merge pull request #10 from fedora-infra/feature/fix-for-puiterwijk `5457d673d `_
41 | - 0.3.2 `3c97c7acb `_
42 |
43 | 0.3.1
44 | -----
45 |
46 | - Adapt this to handle webhook payloads as well as pubsubhubbub. `b821dbf99 `_
47 |
48 | 0.3.0
49 | -----
50 |
51 | - Fix refresh link redirection for @pypingou. `f9d0aad9e `_
52 | - Remove some debugging. `5cd637945 `_
53 | - Construct a nice url when new repos are added. `8b22a8931 `_
54 | - Remove unused utilities. `a98642c10 `_
55 | - Reduce oauth scope. `f093b633b `_
56 | - Modern requests works fine here. `9ceb3110b `_
57 | - That reduced oauth scope doesn't actually work. `b9cc0892d `_
58 | - Update consumer key. `1789b722f `_
59 | - Break toggling out into its own util function. `ad60e5c23 `_
60 | - Port from pubsubhubbub to webhooks. `08cc079cd `_
61 | - These scopes work now. /cc @puiterwijk `fad7394e7 `_
62 | - Just reorganize some of these views.. `1641c5b82 `_
63 |
64 | 0.2.7
65 | -----
66 |
67 | - Get relative urls right for serving behind a proxy. `6d60f5170 `_
68 | - Add new vars to development.ini `935292e2d `_
69 |
70 | 0.2.6
71 | -----
72 |
73 | - Remove currently unused alembic stuff. `655844396 `_
74 |
75 | 0.2.5
76 | -----
77 |
78 | - Include templates and alembic stuff. `92147d6dc `_
79 |
80 | 0.2.4
81 | -----
82 |
83 | - Update to the lastest bootstrap-fedora. `62cc2def2 `_
84 |
85 | 0.2.3
86 | -----
87 |
88 | - Add jquery back in. `8985732f1 `_
89 | - This is gone. `b0e2e309f `_
90 | - Add agpl header notice to each .py file. `52063ac07 `_
91 | - Grammar/style fixes in the README. `ba1a8ead4 `_
92 | - Merge pull request #7 from fedora-infra/feature/review-items `f891d6c4a `_
93 |
94 | 0.2.2
95 | -----
96 |
97 | - Include license fulltext. `29e06e62d `_
98 | - Include tw2.core. `b9483c25e `_
99 | - We don't really require these things. `5259948c3 `_
100 |
101 | 0.2.1
102 | -----
103 |
104 | - Fix inclusion of resources in the dist. `e43d151d5 `_
105 |
106 | 0.2
107 | ---
108 |
109 | - Prune secret.ini. `ec496f86b `_
110 |
--------------------------------------------------------------------------------
/github2fedmsg/static/bootstrap-3.1.1-fedora/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.1.1 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
--------------------------------------------------------------------------------
/github2fedmsg/views/webhooks.py:
--------------------------------------------------------------------------------
1 | # This file is a part of github2fedmsg, a pubsubhubbub to zeromq bridge.
2 | # Copyright (C) 2014, Red Hat, Inc.
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU Affero General Public License as
6 | # published by the Free Software Foundation, either version 3 of the
7 | # License, or (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU Affero General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Affero General Public License
15 | # along with this program. If not, see .
16 |
17 | from pyramid.view import view_config
18 | from pyramid.security import authenticated_userid
19 | from pyramid.httpexceptions import HTTPFound, HTTPUnauthorized, HTTPForbidden
20 |
21 | import github2fedmsg.models as m
22 |
23 | import hashlib
24 | import hmac
25 | import json
26 | import requests
27 |
28 | import logging
29 | from fedora_messaging.api import Message, publish
30 | from fedora_messaging.exceptions import PublishReturned, ConnectionException
31 |
32 | LOGGER = logging.getLogger(__name__)
33 |
34 | # http://developer.github.com/v3/repos/hooks/
35 | github_pubsubhubub_api_url = "https://api.github.com/hub"
36 | # All events: http://developer.github.com/webhooks/#events
37 | github_events = [
38 | # "push", # Any git push to a Repository
39 | # "commit_comment", # Any time a Commit is commented on.
40 | # "create", # Any time a Repository, Branch, or Tag is created.
41 | # "delete", # Any time a Branch or Tag is deleted.
42 | # "release", # Any time a Release is published in the Repository.
43 | # "issues", # Any time an Issue is opened or closed.
44 | # "issue_comment", # Any time an Issue is commented on.
45 | # "pull_request", # Any time a Pull Request is opened, closed, or
46 | # # synchronized (updated due to a new push in the
47 | # # branch that the pull request is tracking).
48 | # "pull_request_review_comment", # Any time a Commit is commented on while
49 | # # inside a Pull Request review (the Files
50 | # # Changed tab).
51 | # "gollum", # Any time a Wiki page is updated.
52 | # "watch", # Any time a User watches the Repository.
53 | # "download", # ?
54 | # "fork", # Any time a Repository is forked.
55 | # "fork_apply", # ?
56 | # "member", # Any time a User is added as a collaborator to a
57 | # # non-Organization Repository.
58 | # "public", # Any time a Repository changes from private to public
59 | # "status", # Any time a Repository has a status update from
60 | # # the API.
61 | # "team_add", # Any time a team is added or modified on a
62 | # # Repository.
63 | # "deployment", # Any time a Repository has a new deployment created
64 | # # from the API.
65 | # "deployment_status",# Any time a deployment for the Repository has a
66 | # #status update from the API.
67 | # "page_build", # Any time a Pages site is built or results in a
68 | # # failed build.
69 | "*", # Any time any event is triggered (Wildcard Event).
70 | ]
71 |
72 |
73 | @view_config(route_name='webhook', request_method="POST", renderer='string')
74 | def webhook(request):
75 | """ Handle github webhook. """
76 |
77 | github_secret = request.registry.settings.get("github.secret")
78 |
79 | hex = hmac.new(github_secret, request.body, hashlib.sha1).hexdigest()
80 | valid_sig = "sha1=%s" % hex
81 |
82 | if 'X-Hub-Signature' not in request.headers:
83 | msg = "No X-Hub-Signature provided"
84 | raise HTTPUnauthorized(msg)
85 |
86 | actual_sig = request.headers['X-Hub-Signature']
87 |
88 | if actual_sig != valid_sig:
89 | msg = "Invalid X-Hub-Signature"
90 | raise HTTPForbidden(msg)
91 |
92 | if 'payload' in request.params:
93 | # pubsubhubbub
94 | payload = request.params['payload']
95 | payload = json.loads(payload)
96 | else:
97 | # whatever webhooks
98 | payload = request.json_body
99 |
100 | event_type = request.headers['X-Github-Event'].lower()
101 |
102 | # github sends us a 'ping' when we first subscribe to let us know that
103 | # it worked.
104 | if event_type == 'ping':
105 | event_type = 'webhook'
106 |
107 | # They don't actually tell us which repo is signed up, so, for
108 | # presentation purposes only, we'll rip out the repo name here and
109 | # build a nice human-clickable url.
110 | tokens = payload['hook']['url'].split('/')
111 | owner, repo = tokens[-4], tokens[-3]
112 | payload['compare'] = 'https://github.com/%s/%s' % (owner, repo)
113 |
114 | # Convert github.watch messages to github.star messages.
115 | # See http://da.gd/sUNkj
116 | if event_type == 'watch':
117 | event_type = 'star'
118 |
119 | # Turn just 'issues' into 'issue.reopened'
120 | if event_type == 'issues':
121 | event_type = 'issue.' + payload['action']
122 |
123 | # Same here
124 | if event_type == 'pull_request':
125 | event_type = 'pull_request.' + payload['action']
126 |
127 | # Make issues comments match our scheme more nicely
128 | if event_type == 'issue_comment':
129 | event_type = 'issue.comment'
130 |
131 | # Strip out a bunch of redundant information that github sends us
132 | payload = prune_useless_urls(payload)
133 |
134 | # Build a little table of github usernames to fas usernames so
135 | # consumers can have an easy time.
136 | fas_usernames = build_fas_lookup(payload)
137 | payload['fas_usernames'] = fas_usernames
138 |
139 | try:
140 | msg = Message(
141 | topic="github.{}".format(event_type),
142 | body=payload,
143 | )
144 | publish(msg)
145 | except PublishReturned as e:
146 | LOGGER.warning(
147 | "Fedora Messaging broker rejected message %s: %s",
148 | msg.id, e
149 | )
150 | except ConnectionException as e:
151 | LOGGER.warning("Error sending message %s: %s", msg.id, e)
152 |
153 | return "OK"
154 |
155 |
156 | def prune_useless_urls(payload):
157 | """ Given *any* github message, strip out unneeded information. """
158 |
159 | for k, v in payload.items():
160 | if k == '_links':
161 | payload['html_url'] = v['html']['href']
162 | del payload[k]
163 | elif isinstance(v, dict):
164 | payload[k] = prune_useless_urls(v)
165 | elif k.endswith('_url') and k not in ['html_url', 'target_url']:
166 | del payload[k]
167 |
168 | return payload
169 |
170 |
171 | def build_fas_lookup(payload):
172 | """ Given *any* github message, build a lookup of github usernames to fas
173 | usernames for it.
174 |
175 | This involves hand coding a bunch of knowledge about github message
176 | formats.
177 | """
178 |
179 | usernames = set()
180 |
181 | # Trawl through every possible corner we can to find github usernames
182 | if 'commits' in payload:
183 | for commit in payload['commits']:
184 | if 'committer' in commit:
185 | if 'username' in commit['committer']:
186 | usernames.add(commit['committer']['username'])
187 | elif 'name' in commit['committer']:
188 | usernames.add(commit['committer']['name'])
189 | if 'author' in commit:
190 | if 'username' in commit['author']:
191 | usernames.add(commit['author']['username'])
192 | elif 'name' in commit['author']:
193 | usernames.add(commit['author']['name'])
194 |
195 | if 'pusher' in payload:
196 | usernames.add(payload['pusher']['name'])
197 |
198 | if 'sender' in payload:
199 | if 'login' in payload['sender']:
200 | usernames.add(payload['sender']['login'])
201 |
202 | if 'forkee' in payload:
203 | if 'login' in payload['forkee']['owner']:
204 | usernames.add(payload['forkee']['owner']['login'])
205 |
206 | if 'repository' in payload:
207 | if 'login' in payload['repository']['owner']:
208 | usernames.add(payload['repository']['owner']['login'])
209 |
210 | # Take all that, and roll it up into a dict mapping those to FAS
211 | mapping = {}
212 | for github_username in usernames:
213 | if not github_username:
214 | continue
215 | user = m.User.query.filter_by(github_username=github_username).first()
216 | if user:
217 | mapping[github_username] = user.username
218 |
219 | return mapping
220 |
221 |
222 | @view_config(name='toggle', context=m.Repo, renderer='json')
223 | def repo_toggle_enabled(request):
224 | # TODO -- someday, learn how to do the __acls__ thing.. :/
225 | userid = authenticated_userid(request)
226 | if userid != request.context.user.username:
227 | if userid not in [
228 | member.username for member in request.context.user.users
229 | ]:
230 | raise HTTPUnauthorized()
231 |
232 | repo = request.context
233 |
234 | # Toggle that attribute on our db model.
235 | repo.enabled = not repo.enabled
236 |
237 | # Now notify github
238 | token = request.user.oauth_access_token
239 | if not token:
240 | raise HTTPForbidden("you need to link your account with github first")
241 |
242 | # We used to do this with pubsubhubbub, but not all of github's oauth
243 | # scopes work there.
244 | # toggle_pubsubhubbub_hooks(request, repo, token)
245 | toggle_webhook_directly(request, repo, token)
246 |
247 | response = {
248 | 'status': 'ok',
249 | 'repo': request.context.__json__(),
250 | 'username': repo.user.username,
251 | 'github_username': repo.user.github_username,
252 | 'enabled': repo.enabled,
253 | }
254 | return response
255 |
256 |
257 | def toggle_pubsubhubbub_hooks(request, repo, token):
258 | data = {
259 | "access_token": token,
260 | "hub.mode": ['unsubscribe', 'subscribe'][repo.enabled],
261 | "hub.callback": request.registry.settings.get("github.callback"),
262 | "hub.secret": request.registry.settings.get("github.secret"),
263 | }
264 |
265 | for event in github_events:
266 | data["hub.topic"] = "https://github.com/%s/%s/events/%s" % (
267 | repo.user.github_username, repo.name, event)
268 | # Subscribe to events via pubsubhubbub
269 | result = requests.post(github_pubsubhubbub_api_url, data=data)
270 |
271 | if result.status_code < 200 or result.status_code > 299:
272 | d = result.json()
273 | d['status_code'] = result.status_code
274 | raise IOError(d)
275 |
276 |
277 | def toggle_webhook_directly(request, repo, token):
278 | actually_enabled = _get_webhook_status_directly(request, repo, token)
279 |
280 | # Here, repo.enabled represents our database knowledge of the hook state.
281 | # "actually_enabled" represents github's own record keeping.
282 |
283 | if repo.enabled and not actually_enabled:
284 | _enable_webhook_directly(request, repo, token)
285 | elif repo.enabled and actually_enabled:
286 | pass # nothing to do
287 | elif not repo.enabled and actually_enabled:
288 | _disable_webhook_directly(request, repo, actually_enabled, token)
289 | elif not repo.enabled and not actually_enabled:
290 | pass # nothing to do
291 |
292 |
293 | def _get_webhook_status_directly(request, repo, token):
294 | auth = {"access_token": token}
295 | url = "https://api.github.com/repos/%s/%s/hooks" % (
296 | repo.user.github_username, repo.name)
297 | result = requests.get(url, params=auth)
298 |
299 | if result.status_code < 200 or result.status_code > 299:
300 | d = result.json()
301 | d['status_code'] = result.status_code
302 | raise IOError(d)
303 |
304 | callback = request.registry.settings.get("github.callback")
305 |
306 | hooks = result.json()
307 | for hook in hooks:
308 | if hook['name'] == 'web' and hook['config'].get('url') == callback:
309 | return hook
310 |
311 | return None
312 |
313 |
314 | def _enable_webhook_directly(request, repo, token):
315 | auth = {"access_token": token}
316 | data = {
317 | "name": "web",
318 | "active": True,
319 | "events": github_events,
320 | "config": {
321 | "url": request.registry.settings.get("github.callback"),
322 | "content_type": "json",
323 | "secret": request.registry.settings.get("github.secret"),
324 | },
325 | }
326 |
327 | headers = {'content-type': 'application/json'}
328 | url = "https://api.github.com/repos/%s/%s/hooks" % (
329 | repo.user.github_username, repo.name)
330 | result = requests.post(
331 | url, params=auth, data=json.dumps(data), headers=headers)
332 |
333 | if result.status_code < 200 or result.status_code > 299:
334 | d = result.json()
335 | d['status_code'] = result.status_code
336 | raise IOError(d)
337 |
338 |
339 | def _disable_webhook_directly(request, repo, hook, token):
340 | auth = {"access_token": token}
341 | url = "https://api.github.com/repos/%s/%s/hooks/%i" % (
342 | repo.user.github_username, repo.name, hook['id'])
343 | requests.delete(url, params=auth)
344 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger-theme-air.css:
--------------------------------------------------------------------------------
1 | @import url("//fonts.googleapis.com/css?family=Raleway:400");
2 | @-webkit-keyframes ui-spinner-rotate-right {
3 | /* line 64, ../../src/sass/messenger-spinner.scss */
4 | 0% {
5 | -webkit-transform: rotate(0deg);
6 | }
7 |
8 | /* line 65, ../../src/sass/messenger-spinner.scss */
9 | 25% {
10 | -webkit-transform: rotate(180deg);
11 | }
12 |
13 | /* line 66, ../../src/sass/messenger-spinner.scss */
14 | 50% {
15 | -webkit-transform: rotate(180deg);
16 | }
17 |
18 | /* line 67, ../../src/sass/messenger-spinner.scss */
19 | 75% {
20 | -webkit-transform: rotate(360deg);
21 | }
22 |
23 | /* line 68, ../../src/sass/messenger-spinner.scss */
24 | 100% {
25 | -webkit-transform: rotate(360deg);
26 | }
27 | }
28 |
29 | @-webkit-keyframes ui-spinner-rotate-left {
30 | /* line 72, ../../src/sass/messenger-spinner.scss */
31 | 0% {
32 | -webkit-transform: rotate(0deg);
33 | }
34 |
35 | /* line 73, ../../src/sass/messenger-spinner.scss */
36 | 25% {
37 | -webkit-transform: rotate(0deg);
38 | }
39 |
40 | /* line 74, ../../src/sass/messenger-spinner.scss */
41 | 50% {
42 | -webkit-transform: rotate(180deg);
43 | }
44 |
45 | /* line 75, ../../src/sass/messenger-spinner.scss */
46 | 75% {
47 | -webkit-transform: rotate(180deg);
48 | }
49 |
50 | /* line 76, ../../src/sass/messenger-spinner.scss */
51 | 100% {
52 | -webkit-transform: rotate(360deg);
53 | }
54 | }
55 |
56 | @-moz-keyframes ui-spinner-rotate-right {
57 | /* line 80, ../../src/sass/messenger-spinner.scss */
58 | 0% {
59 | -moz-transform: rotate(0deg);
60 | }
61 |
62 | /* line 81, ../../src/sass/messenger-spinner.scss */
63 | 25% {
64 | -moz-transform: rotate(180deg);
65 | }
66 |
67 | /* line 82, ../../src/sass/messenger-spinner.scss */
68 | 50% {
69 | -moz-transform: rotate(180deg);
70 | }
71 |
72 | /* line 83, ../../src/sass/messenger-spinner.scss */
73 | 75% {
74 | -moz-transform: rotate(360deg);
75 | }
76 |
77 | /* line 84, ../../src/sass/messenger-spinner.scss */
78 | 100% {
79 | -moz-transform: rotate(360deg);
80 | }
81 | }
82 |
83 | @-moz-keyframes ui-spinner-rotate-left {
84 | /* line 88, ../../src/sass/messenger-spinner.scss */
85 | 0% {
86 | -moz-transform: rotate(0deg);
87 | }
88 |
89 | /* line 89, ../../src/sass/messenger-spinner.scss */
90 | 25% {
91 | -moz-transform: rotate(0deg);
92 | }
93 |
94 | /* line 90, ../../src/sass/messenger-spinner.scss */
95 | 50% {
96 | -moz-transform: rotate(180deg);
97 | }
98 |
99 | /* line 91, ../../src/sass/messenger-spinner.scss */
100 | 75% {
101 | -moz-transform: rotate(180deg);
102 | }
103 |
104 | /* line 92, ../../src/sass/messenger-spinner.scss */
105 | 100% {
106 | -moz-transform: rotate(360deg);
107 | }
108 | }
109 |
110 | @keyframes ui-spinner-rotate-right {
111 | /* line 96, ../../src/sass/messenger-spinner.scss */
112 | 0% {
113 | transform: rotate(0deg);
114 | }
115 |
116 | /* line 97, ../../src/sass/messenger-spinner.scss */
117 | 25% {
118 | transform: rotate(180deg);
119 | }
120 |
121 | /* line 98, ../../src/sass/messenger-spinner.scss */
122 | 50% {
123 | transform: rotate(180deg);
124 | }
125 |
126 | /* line 99, ../../src/sass/messenger-spinner.scss */
127 | 75% {
128 | transform: rotate(360deg);
129 | }
130 |
131 | /* line 100, ../../src/sass/messenger-spinner.scss */
132 | 100% {
133 | transform: rotate(360deg);
134 | }
135 | }
136 |
137 | @keyframes ui-spinner-rotate-left {
138 | /* line 104, ../../src/sass/messenger-spinner.scss */
139 | 0% {
140 | transform: rotate(0deg);
141 | }
142 |
143 | /* line 105, ../../src/sass/messenger-spinner.scss */
144 | 25% {
145 | transform: rotate(0deg);
146 | }
147 |
148 | /* line 106, ../../src/sass/messenger-spinner.scss */
149 | 50% {
150 | transform: rotate(180deg);
151 | }
152 |
153 | /* line 107, ../../src/sass/messenger-spinner.scss */
154 | 75% {
155 | transform: rotate(180deg);
156 | }
157 |
158 | /* line 108, ../../src/sass/messenger-spinner.scss */
159 | 100% {
160 | transform: rotate(360deg);
161 | }
162 | }
163 |
164 | /* line 116, ../../src/sass/messenger-spinner.scss */
165 | .messenger-spinner {
166 | position: relative;
167 | border-radius: 100%;
168 | }
169 | /* line 120, ../../src/sass/messenger-spinner.scss */
170 | ul.messenger.messenger-spinner-active .messenger-spinner .messenger-spinner {
171 | display: block;
172 | }
173 | /* line 124, ../../src/sass/messenger-spinner.scss */
174 | .messenger-spinner .messenger-spinner-side {
175 | width: 50%;
176 | height: 100%;
177 | overflow: hidden;
178 | position: absolute;
179 | }
180 | /* line 130, ../../src/sass/messenger-spinner.scss */
181 | .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
182 | border-radius: 999px;
183 | position: absolute;
184 | width: 100%;
185 | height: 100%;
186 | -webkit-animation-iteration-count: infinite;
187 | -moz-animation-iteration-count: infinite;
188 | -ms-animation-iteration-count: infinite;
189 | -o-animation-iteration-count: infinite;
190 | animation-iteration-count: infinite;
191 | -webkit-animation-timing-function: linear;
192 | -moz-animation-timing-function: linear;
193 | -ms-animation-timing-function: linear;
194 | -o-animation-timing-function: linear;
195 | animation-timing-function: linear;
196 | }
197 | /* line 140, ../../src/sass/messenger-spinner.scss */
198 | .messenger-spinner .messenger-spinner-side-left {
199 | left: 0;
200 | }
201 | /* line 143, ../../src/sass/messenger-spinner.scss */
202 | .messenger-spinner .messenger-spinner-side-left .messenger-spinner-fill {
203 | left: 100%;
204 | border-top-left-radius: 0;
205 | border-bottom-left-radius: 0;
206 | -webkit-animation-name: ui-spinner-rotate-left;
207 | -moz-animation-name: ui-spinner-rotate-left;
208 | -ms-animation-name: ui-spinner-rotate-left;
209 | -o-animation-name: ui-spinner-rotate-left;
210 | animation-name: ui-spinner-rotate-left;
211 | -webkit-transform-origin: 0 50%;
212 | -moz-transform-origin: 0 50%;
213 | -ms-transform-origin: 0 50%;
214 | -o-transform-origin: 0 50%;
215 | transform-origin: 0 50%;
216 | }
217 | /* line 152, ../../src/sass/messenger-spinner.scss */
218 | .messenger-spinner .messenger-spinner-side-right {
219 | left: 50%;
220 | }
221 | /* line 155, ../../src/sass/messenger-spinner.scss */
222 | .messenger-spinner .messenger-spinner-side-right .messenger-spinner-fill {
223 | left: -100%;
224 | border-top-right-radius: 0;
225 | border-bottom-right-radius: 0;
226 | -webkit-animation-name: ui-spinner-rotate-right;
227 | -moz-animation-name: ui-spinner-rotate-right;
228 | -ms-animation-name: ui-spinner-rotate-right;
229 | -o-animation-name: ui-spinner-rotate-right;
230 | animation-name: ui-spinner-rotate-right;
231 | -webkit-transform-origin: 100% 50%;
232 | -moz-transform-origin: 100% 50%;
233 | -ms-transform-origin: 100% 50%;
234 | -o-transform-origin: 100% 50%;
235 | transform-origin: 100% 50%;
236 | }
237 |
238 | /* line 16, ../../src/sass/messenger-theme-air.sass */
239 | ul.messenger-theme-air {
240 | -moz-user-select: none;
241 | -webkit-user-select: none;
242 | -o-user-select: none;
243 | user-select: none;
244 | font-family: "Raleway", sans-serif;
245 | }
246 | /* line 20, ../../src/sass/messenger-theme-air.sass */
247 | ul.messenger-theme-air .messenger-message {
248 | -webkit-transition: background-color 0.4s;
249 | -moz-transition: background-color 0.4s;
250 | -o-transition: background-color 0.4s;
251 | transition: background-color 0.4s;
252 | -webkit-border-radius: 5px;
253 | -moz-border-radius: 5px;
254 | -ms-border-radius: 5px;
255 | -o-border-radius: 5px;
256 | border-radius: 5px;
257 | -webkit-box-shadow: inset 0 0 0 1px white, inset 0 2px white, 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px rgba(0, 0, 0, 0.2);
258 | -moz-box-shadow: inset 0 0 0 1px white, inset 0 2px white, 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px rgba(0, 0, 0, 0.2);
259 | box-shadow: inset 0 0 0 1px white, inset 0 2px white, 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px rgba(0, 0, 0, 0.2);
260 | border: 0px;
261 | background-color: rgba(255, 255, 255, 0.8);
262 | position: relative;
263 | margin-bottom: 1em;
264 | font-size: 13px;
265 | color: #666666;
266 | font-weight: 500;
267 | padding: 10px 30px 11px 46px;
268 | }
269 | /* line 33, ../../src/sass/messenger-theme-air.sass */
270 | ul.messenger-theme-air .messenger-message:hover {
271 | background-color: white;
272 | }
273 | /* line 36, ../../src/sass/messenger-theme-air.sass */
274 | ul.messenger-theme-air .messenger-message .messenger-close {
275 | position: absolute;
276 | top: 0px;
277 | right: 0px;
278 | color: #888888;
279 | opacity: 1;
280 | font-weight: bold;
281 | display: block;
282 | font-size: 20px;
283 | line-height: 20px;
284 | padding: 8px 10px 7px 7px;
285 | cursor: pointer;
286 | background: transparent;
287 | border: 0;
288 | -webkit-appearance: none;
289 | }
290 | /* line 52, ../../src/sass/messenger-theme-air.sass */
291 | ul.messenger-theme-air .messenger-message .messenger-close:hover {
292 | color: #444444;
293 | }
294 | /* line 55, ../../src/sass/messenger-theme-air.sass */
295 | ul.messenger-theme-air .messenger-message .messenger-close:active {
296 | color: #222222;
297 | }
298 | /* line 58, ../../src/sass/messenger-theme-air.sass */
299 | ul.messenger-theme-air .messenger-message .messenger-actions {
300 | float: none;
301 | margin-top: 10px;
302 | }
303 | /* line 62, ../../src/sass/messenger-theme-air.sass */
304 | ul.messenger-theme-air .messenger-message .messenger-actions a {
305 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.05);
306 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.05);
307 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.05);
308 | -webkit-border-radius: 4px;
309 | -moz-border-radius: 4px;
310 | -ms-border-radius: 4px;
311 | -o-border-radius: 4px;
312 | border-radius: 4px;
313 | text-decoration: none;
314 | display: inline-block;
315 | padding: 10px;
316 | color: #888888;
317 | margin-right: 10px;
318 | padding: 3px 10px 5px;
319 | text-transform: capitalize;
320 | }
321 | /* line 73, ../../src/sass/messenger-theme-air.sass */
322 | ul.messenger-theme-air .messenger-message .messenger-actions a:hover {
323 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15);
324 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15);
325 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1), inset 0px 1px rgba(255, 255, 255, 0.15);
326 | color: #444444;
327 | }
328 | /* line 77, ../../src/sass/messenger-theme-air.sass */
329 | ul.messenger-theme-air .messenger-message .messenger-actions a:active {
330 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.18), inset 0px 1px rgba(0, 0, 0, 0.05);
331 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.18), inset 0px 1px rgba(0, 0, 0, 0.05);
332 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.18), inset 0px 1px rgba(0, 0, 0, 0.05);
333 | background: rgba(0, 0, 0, 0.04);
334 | color: #444444;
335 | }
336 | /* line 82, ../../src/sass/messenger-theme-air.sass */
337 | ul.messenger-theme-air .messenger-message .messenger-actions .messenger-phrase {
338 | display: none;
339 | }
340 | /* line 85, ../../src/sass/messenger-theme-air.sass */
341 | ul.messenger-theme-air .messenger-message .messenger-message-inner:before {
342 | -webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
343 | -moz-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
344 | box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
345 | -webkit-border-radius: 50%;
346 | -moz-border-radius: 50%;
347 | -ms-border-radius: 50%;
348 | -o-border-radius: 50%;
349 | border-radius: 50%;
350 | position: absolute;
351 | left: 17px;
352 | display: block;
353 | content: " ";
354 | top: 50%;
355 | margin-top: -8px;
356 | height: 13px;
357 | width: 13px;
358 | z-index: 20;
359 | }
360 | /* line 99, ../../src/sass/messenger-theme-air.sass */
361 | ul.messenger-theme-air .messenger-message.alert-success .messenger-message-inner:before {
362 | background-color: #5fca4a;
363 | }
364 | /* line 32, ../../src/sass/messenger-spinner.scss */
365 | ul.messenger-theme-air .messenger-message.alert-error.messenger-retry-soon .messenger-spinner {
366 | width: 24px;
367 | height: 24px;
368 | background: transparent;
369 | }
370 | /* line 37, ../../src/sass/messenger-spinner.scss */
371 | ul.messenger-theme-air .messenger-message.alert-error.messenger-retry-soon .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
372 | background: #dd6a45;
373 | -webkit-animation-duration: 20s;
374 | -moz-animation-duration: 20s;
375 | -ms-animation-duration: 20s;
376 | -o-animation-duration: 20s;
377 | animation-duration: 20s;
378 | opacity: 1;
379 | }
380 | /* line 45, ../../src/sass/messenger-spinner.scss */
381 | ul.messenger-theme-air .messenger-message.alert-error.messenger-retry-soon .messenger-spinner:after {
382 | content: "";
383 | background: white;
384 | position: absolute;
385 | width: 19px;
386 | height: 19px;
387 | border-radius: 50%;
388 | top: 2px;
389 | left: 2px;
390 | display: block;
391 | }
392 | /* line 32, ../../src/sass/messenger-spinner.scss */
393 | ul.messenger-theme-air .messenger-message.alert-error.messenger-retry-later .messenger-spinner {
394 | width: 24px;
395 | height: 24px;
396 | background: transparent;
397 | }
398 | /* line 37, ../../src/sass/messenger-spinner.scss */
399 | ul.messenger-theme-air .messenger-message.alert-error.messenger-retry-later .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
400 | background: #dd6a45;
401 | -webkit-animation-duration: 600s;
402 | -moz-animation-duration: 600s;
403 | -ms-animation-duration: 600s;
404 | -o-animation-duration: 600s;
405 | animation-duration: 600s;
406 | opacity: 1;
407 | }
408 | /* line 45, ../../src/sass/messenger-spinner.scss */
409 | ul.messenger-theme-air .messenger-message.alert-error.messenger-retry-later .messenger-spinner:after {
410 | content: "";
411 | background: white;
412 | position: absolute;
413 | width: 19px;
414 | height: 19px;
415 | border-radius: 50%;
416 | top: 2px;
417 | left: 2px;
418 | display: block;
419 | }
420 | /* line 109, ../../src/sass/messenger-theme-air.sass */
421 | ul.messenger-theme-air .messenger-message.alert-error .messenger-message-inner:before {
422 | background-color: #dd6a45;
423 | }
424 | /* line 113, ../../src/sass/messenger-theme-air.sass */
425 | ul.messenger-theme-air .messenger-message.alert-info .messenger-message-inner:before {
426 | background-color: #61c4b8;
427 | }
428 | /* line 116, ../../src/sass/messenger-theme-air.sass */
429 | ul.messenger-theme-air .messenger-spinner {
430 | display: block;
431 | position: absolute;
432 | left: 12px;
433 | top: 50%;
434 | margin-top: -13px;
435 | z-index: 999;
436 | height: 24px;
437 | width: 24px;
438 | z-index: 10;
439 | }
440 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger-theme-flat.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes ui-spinner-rotate-right {
2 | /* line 64, ../../src/sass/messenger-spinner.scss */
3 | 0% {
4 | -webkit-transform: rotate(0deg);
5 | }
6 |
7 | /* line 65, ../../src/sass/messenger-spinner.scss */
8 | 25% {
9 | -webkit-transform: rotate(180deg);
10 | }
11 |
12 | /* line 66, ../../src/sass/messenger-spinner.scss */
13 | 50% {
14 | -webkit-transform: rotate(180deg);
15 | }
16 |
17 | /* line 67, ../../src/sass/messenger-spinner.scss */
18 | 75% {
19 | -webkit-transform: rotate(360deg);
20 | }
21 |
22 | /* line 68, ../../src/sass/messenger-spinner.scss */
23 | 100% {
24 | -webkit-transform: rotate(360deg);
25 | }
26 | }
27 |
28 | @-webkit-keyframes ui-spinner-rotate-left {
29 | /* line 72, ../../src/sass/messenger-spinner.scss */
30 | 0% {
31 | -webkit-transform: rotate(0deg);
32 | }
33 |
34 | /* line 73, ../../src/sass/messenger-spinner.scss */
35 | 25% {
36 | -webkit-transform: rotate(0deg);
37 | }
38 |
39 | /* line 74, ../../src/sass/messenger-spinner.scss */
40 | 50% {
41 | -webkit-transform: rotate(180deg);
42 | }
43 |
44 | /* line 75, ../../src/sass/messenger-spinner.scss */
45 | 75% {
46 | -webkit-transform: rotate(180deg);
47 | }
48 |
49 | /* line 76, ../../src/sass/messenger-spinner.scss */
50 | 100% {
51 | -webkit-transform: rotate(360deg);
52 | }
53 | }
54 |
55 | @-moz-keyframes ui-spinner-rotate-right {
56 | /* line 80, ../../src/sass/messenger-spinner.scss */
57 | 0% {
58 | -moz-transform: rotate(0deg);
59 | }
60 |
61 | /* line 81, ../../src/sass/messenger-spinner.scss */
62 | 25% {
63 | -moz-transform: rotate(180deg);
64 | }
65 |
66 | /* line 82, ../../src/sass/messenger-spinner.scss */
67 | 50% {
68 | -moz-transform: rotate(180deg);
69 | }
70 |
71 | /* line 83, ../../src/sass/messenger-spinner.scss */
72 | 75% {
73 | -moz-transform: rotate(360deg);
74 | }
75 |
76 | /* line 84, ../../src/sass/messenger-spinner.scss */
77 | 100% {
78 | -moz-transform: rotate(360deg);
79 | }
80 | }
81 |
82 | @-moz-keyframes ui-spinner-rotate-left {
83 | /* line 88, ../../src/sass/messenger-spinner.scss */
84 | 0% {
85 | -moz-transform: rotate(0deg);
86 | }
87 |
88 | /* line 89, ../../src/sass/messenger-spinner.scss */
89 | 25% {
90 | -moz-transform: rotate(0deg);
91 | }
92 |
93 | /* line 90, ../../src/sass/messenger-spinner.scss */
94 | 50% {
95 | -moz-transform: rotate(180deg);
96 | }
97 |
98 | /* line 91, ../../src/sass/messenger-spinner.scss */
99 | 75% {
100 | -moz-transform: rotate(180deg);
101 | }
102 |
103 | /* line 92, ../../src/sass/messenger-spinner.scss */
104 | 100% {
105 | -moz-transform: rotate(360deg);
106 | }
107 | }
108 |
109 | @keyframes ui-spinner-rotate-right {
110 | /* line 96, ../../src/sass/messenger-spinner.scss */
111 | 0% {
112 | transform: rotate(0deg);
113 | }
114 |
115 | /* line 97, ../../src/sass/messenger-spinner.scss */
116 | 25% {
117 | transform: rotate(180deg);
118 | }
119 |
120 | /* line 98, ../../src/sass/messenger-spinner.scss */
121 | 50% {
122 | transform: rotate(180deg);
123 | }
124 |
125 | /* line 99, ../../src/sass/messenger-spinner.scss */
126 | 75% {
127 | transform: rotate(360deg);
128 | }
129 |
130 | /* line 100, ../../src/sass/messenger-spinner.scss */
131 | 100% {
132 | transform: rotate(360deg);
133 | }
134 | }
135 |
136 | @keyframes ui-spinner-rotate-left {
137 | /* line 104, ../../src/sass/messenger-spinner.scss */
138 | 0% {
139 | transform: rotate(0deg);
140 | }
141 |
142 | /* line 105, ../../src/sass/messenger-spinner.scss */
143 | 25% {
144 | transform: rotate(0deg);
145 | }
146 |
147 | /* line 106, ../../src/sass/messenger-spinner.scss */
148 | 50% {
149 | transform: rotate(180deg);
150 | }
151 |
152 | /* line 107, ../../src/sass/messenger-spinner.scss */
153 | 75% {
154 | transform: rotate(180deg);
155 | }
156 |
157 | /* line 108, ../../src/sass/messenger-spinner.scss */
158 | 100% {
159 | transform: rotate(360deg);
160 | }
161 | }
162 |
163 | /* line 116, ../../src/sass/messenger-spinner.scss */
164 | .messenger-spinner {
165 | position: relative;
166 | border-radius: 100%;
167 | }
168 | /* line 120, ../../src/sass/messenger-spinner.scss */
169 | ul.messenger.messenger-spinner-active .messenger-spinner .messenger-spinner {
170 | display: block;
171 | }
172 | /* line 124, ../../src/sass/messenger-spinner.scss */
173 | .messenger-spinner .messenger-spinner-side {
174 | width: 50%;
175 | height: 100%;
176 | overflow: hidden;
177 | position: absolute;
178 | }
179 | /* line 130, ../../src/sass/messenger-spinner.scss */
180 | .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
181 | border-radius: 999px;
182 | position: absolute;
183 | width: 100%;
184 | height: 100%;
185 | -webkit-animation-iteration-count: infinite;
186 | -moz-animation-iteration-count: infinite;
187 | -ms-animation-iteration-count: infinite;
188 | -o-animation-iteration-count: infinite;
189 | animation-iteration-count: infinite;
190 | -webkit-animation-timing-function: linear;
191 | -moz-animation-timing-function: linear;
192 | -ms-animation-timing-function: linear;
193 | -o-animation-timing-function: linear;
194 | animation-timing-function: linear;
195 | }
196 | /* line 140, ../../src/sass/messenger-spinner.scss */
197 | .messenger-spinner .messenger-spinner-side-left {
198 | left: 0;
199 | }
200 | /* line 143, ../../src/sass/messenger-spinner.scss */
201 | .messenger-spinner .messenger-spinner-side-left .messenger-spinner-fill {
202 | left: 100%;
203 | border-top-left-radius: 0;
204 | border-bottom-left-radius: 0;
205 | -webkit-animation-name: ui-spinner-rotate-left;
206 | -moz-animation-name: ui-spinner-rotate-left;
207 | -ms-animation-name: ui-spinner-rotate-left;
208 | -o-animation-name: ui-spinner-rotate-left;
209 | animation-name: ui-spinner-rotate-left;
210 | -webkit-transform-origin: 0 50%;
211 | -moz-transform-origin: 0 50%;
212 | -ms-transform-origin: 0 50%;
213 | -o-transform-origin: 0 50%;
214 | transform-origin: 0 50%;
215 | }
216 | /* line 152, ../../src/sass/messenger-spinner.scss */
217 | .messenger-spinner .messenger-spinner-side-right {
218 | left: 50%;
219 | }
220 | /* line 155, ../../src/sass/messenger-spinner.scss */
221 | .messenger-spinner .messenger-spinner-side-right .messenger-spinner-fill {
222 | left: -100%;
223 | border-top-right-radius: 0;
224 | border-bottom-right-radius: 0;
225 | -webkit-animation-name: ui-spinner-rotate-right;
226 | -moz-animation-name: ui-spinner-rotate-right;
227 | -ms-animation-name: ui-spinner-rotate-right;
228 | -o-animation-name: ui-spinner-rotate-right;
229 | animation-name: ui-spinner-rotate-right;
230 | -webkit-transform-origin: 100% 50%;
231 | -moz-transform-origin: 100% 50%;
232 | -ms-transform-origin: 100% 50%;
233 | -o-transform-origin: 100% 50%;
234 | transform-origin: 100% 50%;
235 | }
236 |
237 | /* line 15, ../../src/sass/messenger-theme-flat.sass */
238 | ul.messenger-theme-flat {
239 | -webkit-border-radius: 4px;
240 | -moz-border-radius: 4px;
241 | -ms-border-radius: 4px;
242 | -o-border-radius: 4px;
243 | border-radius: 4px;
244 | -moz-user-select: none;
245 | -webkit-user-select: none;
246 | -o-user-select: none;
247 | user-select: none;
248 | background: #404040;
249 | }
250 | /* line 20, ../../src/sass/messenger-theme-flat.sass */
251 | ul.messenger-theme-flat.messenger-empty {
252 | display: none;
253 | }
254 | /* line 23, ../../src/sass/messenger-theme-flat.sass */
255 | ul.messenger-theme-flat .messenger-message {
256 | -webkit-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px #292929;
257 | -moz-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px #292929;
258 | box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px #292929;
259 | -webkit-border-radius: 0px;
260 | -moz-border-radius: 0px;
261 | -ms-border-radius: 0px;
262 | -o-border-radius: 0px;
263 | border-radius: 0px;
264 | position: relative;
265 | border: 0px;
266 | margin-bottom: 0px;
267 | font-size: 13px;
268 | background: transparent;
269 | color: #f0f0f0;
270 | font-weight: 500;
271 | padding: 10px 30px 13px 65px;
272 | }
273 | /* line 35, ../../src/sass/messenger-theme-flat.sass */
274 | ul.messenger-theme-flat .messenger-message .messenger-close {
275 | position: absolute;
276 | top: 0px;
277 | right: 0px;
278 | color: #888888;
279 | opacity: 1;
280 | font-weight: bold;
281 | display: block;
282 | font-size: 20px;
283 | line-height: 20px;
284 | padding: 8px 10px 7px 7px;
285 | cursor: pointer;
286 | background: transparent;
287 | border: 0;
288 | -webkit-appearance: none;
289 | }
290 | /* line 51, ../../src/sass/messenger-theme-flat.sass */
291 | ul.messenger-theme-flat .messenger-message .messenger-close:hover {
292 | color: #bbbbbb;
293 | }
294 | /* line 54, ../../src/sass/messenger-theme-flat.sass */
295 | ul.messenger-theme-flat .messenger-message .messenger-close:active {
296 | color: #777777;
297 | }
298 | /* line 57, ../../src/sass/messenger-theme-flat.sass */
299 | ul.messenger-theme-flat .messenger-message .messenger-actions {
300 | float: none;
301 | margin-top: 10px;
302 | }
303 | /* line 61, ../../src/sass/messenger-theme-flat.sass */
304 | ul.messenger-theme-flat .messenger-message .messenger-actions a {
305 | -webkit-border-radius: 4px;
306 | -moz-border-radius: 4px;
307 | -ms-border-radius: 4px;
308 | -o-border-radius: 4px;
309 | border-radius: 4px;
310 | text-decoration: none;
311 | color: #aaaaaa;
312 | background: #2e2e2e;
313 | display: inline-block;
314 | padding: 10px;
315 | margin-right: 10px;
316 | padding: 4px 11px 6px;
317 | text-transform: capitalize;
318 | }
319 | /* line 72, ../../src/sass/messenger-theme-flat.sass */
320 | ul.messenger-theme-flat .messenger-message .messenger-actions a:hover {
321 | color: #f0f0f0;
322 | background: #2e2e2e;
323 | }
324 | /* line 76, ../../src/sass/messenger-theme-flat.sass */
325 | ul.messenger-theme-flat .messenger-message .messenger-actions a:active {
326 | background: #292929;
327 | color: #aaaaaa;
328 | }
329 | /* line 80, ../../src/sass/messenger-theme-flat.sass */
330 | ul.messenger-theme-flat .messenger-message .messenger-actions .messenger-phrase {
331 | display: none;
332 | }
333 | /* line 83, ../../src/sass/messenger-theme-flat.sass */
334 | ul.messenger-theme-flat .messenger-message .messenger-message-inner:before {
335 | -webkit-border-radius: 50%;
336 | -moz-border-radius: 50%;
337 | -ms-border-radius: 50%;
338 | -o-border-radius: 50%;
339 | border-radius: 50%;
340 | position: absolute;
341 | left: 17px;
342 | display: block;
343 | content: " ";
344 | top: 50%;
345 | margin-top: -8px;
346 | height: 13px;
347 | width: 13px;
348 | z-index: 20;
349 | }
350 | /* line 95, ../../src/sass/messenger-theme-flat.sass */
351 | ul.messenger-theme-flat .messenger-message.alert-success .messenger-message-inner:before {
352 | background: #5fca4a;
353 | }
354 | /* line 98, ../../src/sass/messenger-theme-flat.sass */
355 | ul.messenger-theme-flat .messenger-message.alert-info .messenger-message-inner:before {
356 | background: #61c4b8;
357 | }
358 | /* line 103, ../../src/sass/messenger-theme-flat.sass */
359 | ul.messenger-theme-flat .messenger-message.alert-error .messenger-message-inner:before {
360 | background: #dd6a45;
361 | }
362 | /* line 32, ../../src/sass/messenger-spinner.scss */
363 | ul.messenger-theme-flat .messenger-message.alert-error.messenger-retry-soon .messenger-spinner {
364 | width: 32px;
365 | height: 32px;
366 | background: transparent;
367 | }
368 | /* line 37, ../../src/sass/messenger-spinner.scss */
369 | ul.messenger-theme-flat .messenger-message.alert-error.messenger-retry-soon .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
370 | background: #dd6a45;
371 | -webkit-animation-duration: 20s;
372 | -moz-animation-duration: 20s;
373 | -ms-animation-duration: 20s;
374 | -o-animation-duration: 20s;
375 | animation-duration: 20s;
376 | opacity: 1;
377 | }
378 | /* line 45, ../../src/sass/messenger-spinner.scss */
379 | ul.messenger-theme-flat .messenger-message.alert-error.messenger-retry-soon .messenger-spinner:after {
380 | content: "";
381 | background: #292929;
382 | position: absolute;
383 | width: 26px;
384 | height: 26px;
385 | border-radius: 50%;
386 | top: 3px;
387 | left: 3px;
388 | display: block;
389 | }
390 | /* line 32, ../../src/sass/messenger-spinner.scss */
391 | ul.messenger-theme-flat .messenger-message.alert-error.messenger-retry-later .messenger-spinner {
392 | width: 32px;
393 | height: 32px;
394 | background: transparent;
395 | }
396 | /* line 37, ../../src/sass/messenger-spinner.scss */
397 | ul.messenger-theme-flat .messenger-message.alert-error.messenger-retry-later .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
398 | background: #dd6a45;
399 | -webkit-animation-duration: 600s;
400 | -moz-animation-duration: 600s;
401 | -ms-animation-duration: 600s;
402 | -o-animation-duration: 600s;
403 | animation-duration: 600s;
404 | opacity: 1;
405 | }
406 | /* line 45, ../../src/sass/messenger-spinner.scss */
407 | ul.messenger-theme-flat .messenger-message.alert-error.messenger-retry-later .messenger-spinner:after {
408 | content: "";
409 | background: #292929;
410 | position: absolute;
411 | width: 26px;
412 | height: 26px;
413 | border-radius: 50%;
414 | top: 3px;
415 | left: 3px;
416 | display: block;
417 | }
418 | /* line 114, ../../src/sass/messenger-theme-flat.sass */
419 | ul.messenger-theme-flat .messenger-message-slot.messenger-last .messenger-message {
420 | -webkit-border-radius: 4px 4px 0px 0px;
421 | -moz-border-radius: 4px 4px 0px 0px;
422 | -ms-border-radius: 4px 4px 0px 0px;
423 | -o-border-radius: 4px 4px 0px 0px;
424 | border-radius: 4px 4px 0px 0px;
425 | -webkit-box-shadow: inset 48px 0px 0px #292929;
426 | -moz-box-shadow: inset 48px 0px 0px #292929;
427 | box-shadow: inset 48px 0px 0px #292929;
428 | }
429 | /* line 118, ../../src/sass/messenger-theme-flat.sass */
430 | ul.messenger-theme-flat .messenger-message-slot.messenger-first .messenger-message {
431 | -webkit-border-radius: 0px 0px 4px 4px;
432 | -moz-border-radius: 0px 0px 4px 4px;
433 | -ms-border-radius: 0px 0px 4px 4px;
434 | -o-border-radius: 0px 0px 4px 4px;
435 | border-radius: 0px 0px 4px 4px;
436 | -webkit-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px #292929;
437 | -moz-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px #292929;
438 | box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px #292929;
439 | }
440 | /* line 122, ../../src/sass/messenger-theme-flat.sass */
441 | ul.messenger-theme-flat .messenger-message-slot.messenger-first.messenger-last .messenger-message {
442 | -webkit-border-radius: 4px;
443 | -moz-border-radius: 4px;
444 | -ms-border-radius: 4px;
445 | -o-border-radius: 4px;
446 | border-radius: 4px;
447 | -webkit-box-shadow: inset 48px 0px 0px #292929;
448 | -moz-box-shadow: inset 48px 0px 0px #292929;
449 | box-shadow: inset 48px 0px 0px #292929;
450 | }
451 | /* line 126, ../../src/sass/messenger-theme-flat.sass */
452 | ul.messenger-theme-flat .messenger-spinner {
453 | display: block;
454 | position: absolute;
455 | left: 7px;
456 | top: 50%;
457 | margin-top: -18px;
458 | z-index: 999;
459 | height: 32px;
460 | width: 32px;
461 | z-index: 10;
462 | }
463 |
--------------------------------------------------------------------------------
/github2fedmsg/static/bootstrap-3.1.1-fedora/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.1.1 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | .btn-default,
8 | .btn-primary,
9 | .btn-success,
10 | .btn-info,
11 | .btn-warning,
12 | .btn-danger {
13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
16 | }
17 | .btn-default:active,
18 | .btn-primary:active,
19 | .btn-success:active,
20 | .btn-info:active,
21 | .btn-warning:active,
22 | .btn-danger:active,
23 | .btn-default.active,
24 | .btn-primary.active,
25 | .btn-success.active,
26 | .btn-info.active,
27 | .btn-warning.active,
28 | .btn-danger.active {
29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
31 | }
32 | .btn:active,
33 | .btn.active {
34 | background-image: none;
35 | }
36 | .btn-default {
37 | text-shadow: 0 1px 0 #fff;
38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
39 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
40 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
41 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
42 | background-repeat: repeat-x;
43 | border-color: #dbdbdb;
44 | border-color: #ccc;
45 | }
46 | .btn-default:hover,
47 | .btn-default:focus {
48 | background-color: #e0e0e0;
49 | background-position: 0 -15px;
50 | }
51 | .btn-default:active,
52 | .btn-default.active {
53 | background-color: #e0e0e0;
54 | border-color: #dbdbdb;
55 | }
56 | .btn-primary {
57 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
58 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
59 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
60 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
61 | background-repeat: repeat-x;
62 | border-color: #2b669a;
63 | }
64 | .btn-primary:hover,
65 | .btn-primary:focus {
66 | background-color: #2d6ca2;
67 | background-position: 0 -15px;
68 | }
69 | .btn-primary:active,
70 | .btn-primary.active {
71 | background-color: #2d6ca2;
72 | border-color: #2b669a;
73 | }
74 | .btn-success {
75 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
76 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
78 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
79 | background-repeat: repeat-x;
80 | border-color: #3e8f3e;
81 | }
82 | .btn-success:hover,
83 | .btn-success:focus {
84 | background-color: #419641;
85 | background-position: 0 -15px;
86 | }
87 | .btn-success:active,
88 | .btn-success.active {
89 | background-color: #419641;
90 | border-color: #3e8f3e;
91 | }
92 | .btn-info {
93 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
94 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
95 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
96 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
97 | background-repeat: repeat-x;
98 | border-color: #28a4c9;
99 | }
100 | .btn-info:hover,
101 | .btn-info:focus {
102 | background-color: #2aabd2;
103 | background-position: 0 -15px;
104 | }
105 | .btn-info:active,
106 | .btn-info.active {
107 | background-color: #2aabd2;
108 | border-color: #28a4c9;
109 | }
110 | .btn-warning {
111 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
112 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
113 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
114 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
115 | background-repeat: repeat-x;
116 | border-color: #e38d13;
117 | }
118 | .btn-warning:hover,
119 | .btn-warning:focus {
120 | background-color: #eb9316;
121 | background-position: 0 -15px;
122 | }
123 | .btn-warning:active,
124 | .btn-warning.active {
125 | background-color: #eb9316;
126 | border-color: #e38d13;
127 | }
128 | .btn-danger {
129 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
130 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
131 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
132 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
133 | background-repeat: repeat-x;
134 | border-color: #b92c28;
135 | }
136 | .btn-danger:hover,
137 | .btn-danger:focus {
138 | background-color: #c12e2a;
139 | background-position: 0 -15px;
140 | }
141 | .btn-danger:active,
142 | .btn-danger.active {
143 | background-color: #c12e2a;
144 | border-color: #b92c28;
145 | }
146 | .thumbnail,
147 | .img-thumbnail {
148 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
149 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
150 | }
151 | .dropdown-menu > li > a:hover,
152 | .dropdown-menu > li > a:focus {
153 | background-color: #e8e8e8;
154 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
155 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
156 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
157 | background-repeat: repeat-x;
158 | }
159 | .dropdown-menu > .active > a,
160 | .dropdown-menu > .active > a:hover,
161 | .dropdown-menu > .active > a:focus {
162 | background-color: #357ebd;
163 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
164 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
165 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
166 | background-repeat: repeat-x;
167 | }
168 | .navbar-default {
169 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
170 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
171 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
172 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
173 | background-repeat: repeat-x;
174 | border-radius: 4px;
175 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
176 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
177 | }
178 | .navbar-default .navbar-nav > .active > a {
179 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
180 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
181 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
182 | background-repeat: repeat-x;
183 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
184 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
185 | }
186 | .navbar-brand,
187 | .navbar-nav > li > a {
188 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
189 | }
190 | .navbar-inverse {
191 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
192 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
193 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
194 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
195 | background-repeat: repeat-x;
196 | }
197 | .navbar-inverse .navbar-nav > .active > a {
198 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
199 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
201 | background-repeat: repeat-x;
202 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
203 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
204 | }
205 | .navbar-inverse .navbar-brand,
206 | .navbar-inverse .navbar-nav > li > a {
207 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
208 | }
209 | .navbar-static-top,
210 | .navbar-fixed-top,
211 | .navbar-fixed-bottom {
212 | border-radius: 0;
213 | }
214 | .alert {
215 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
216 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
217 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
218 | }
219 | .alert-success {
220 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
221 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
222 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
223 | background-repeat: repeat-x;
224 | border-color: #b2dba1;
225 | }
226 | .alert-info {
227 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
228 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
230 | background-repeat: repeat-x;
231 | border-color: #9acfea;
232 | }
233 | .alert-warning {
234 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
235 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
236 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
237 | background-repeat: repeat-x;
238 | border-color: #f5e79e;
239 | }
240 | .alert-danger {
241 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
242 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
243 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
244 | background-repeat: repeat-x;
245 | border-color: #dca7a7;
246 | }
247 | .progress {
248 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
249 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
250 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
251 | background-repeat: repeat-x;
252 | }
253 | .progress-bar {
254 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
255 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
256 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
257 | background-repeat: repeat-x;
258 | }
259 | .progress-bar-success {
260 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
261 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
262 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
263 | background-repeat: repeat-x;
264 | }
265 | .progress-bar-info {
266 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
267 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
268 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
269 | background-repeat: repeat-x;
270 | }
271 | .progress-bar-warning {
272 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
273 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
274 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
275 | background-repeat: repeat-x;
276 | }
277 | .progress-bar-danger {
278 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
279 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
280 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
281 | background-repeat: repeat-x;
282 | }
283 | .list-group {
284 | border-radius: 4px;
285 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
286 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
287 | }
288 | .list-group-item.active,
289 | .list-group-item.active:hover,
290 | .list-group-item.active:focus {
291 | text-shadow: 0 -1px 0 #3071a9;
292 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
293 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
294 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
295 | background-repeat: repeat-x;
296 | border-color: #3278b3;
297 | }
298 | .panel {
299 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
300 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
301 | }
302 | .panel-default > .panel-heading {
303 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
304 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
306 | background-repeat: repeat-x;
307 | }
308 | .panel-primary > .panel-heading {
309 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
310 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
311 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
312 | background-repeat: repeat-x;
313 | }
314 | .panel-success > .panel-heading {
315 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
316 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
317 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
318 | background-repeat: repeat-x;
319 | }
320 | .panel-info > .panel-heading {
321 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
322 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
323 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
324 | background-repeat: repeat-x;
325 | }
326 | .panel-warning > .panel-heading {
327 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
328 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
329 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
330 | background-repeat: repeat-x;
331 | }
332 | .panel-danger > .panel-heading {
333 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
334 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
336 | background-repeat: repeat-x;
337 | }
338 | .well {
339 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
340 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
341 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
342 | background-repeat: repeat-x;
343 | border-color: #dcdcdc;
344 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
345 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
346 | }
347 | /*# sourceMappingURL=bootstrap-theme.css.map */
348 |
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/js/messenger.min.js:
--------------------------------------------------------------------------------
1 | /*! messenger 1.4.1 */
2 | (function(){var e,t=window.Messenger;e=window.Messenger=function(){return e._call.apply(this,arguments)},window.Messenger.noConflict=function(){return window.Messenger=t,e}})(),window.Messenger._=function(){if(window._)return window._;var e=Array.prototype,t=Object.prototype,n=Function.prototype,s=(e.push,e.slice),r=(e.concat,t.toString);t.hasOwnProperty;var o=e.forEach,i=(e.map,e.reduce,e.reduceRight,e.filter),a=(e.every,e.some,e.indexOf,e.lastIndexOf,Array.isArray,Object.keys),l=n.bind,u={},c={},h=u.each=u.forEach=function(e,t,n){if(null!=e)if(o&&e.forEach===o)e.forEach(t,n);else if(e.length===+e.length){for(var s=0,r=e.length;r>s;s++)if(t.call(n,e[s],s,e)===c)return}else for(var i in e)if(u.has(e,i)&&t.call(n,e[i],i,e)===c)return};u.result=function(e,t){if(null==e)return null;var n=e[t];return u.isFunction(n)?n.call(e):n},u.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}};var p=0;return u.uniqueId=function(e){var t=++p+"";return e?e+t:t},u.filter=u.select=function(e,t,n){var s=[];return null==e?s:i&&e.filter===i?e.filter(t,n):(h(e,function(e,r,o){t.call(n,e,r,o)&&(s[s.length]=e)}),s)},h(["Arguments","Function","String","Number","Date","RegExp"],function(e){u["is"+e]=function(t){return r.call(t)=="[object "+e+"]"}}),u.defaults=function(e){return h(s.call(arguments,1),function(t){if(t)for(var n in t)null==e[n]&&(e[n]=t[n])}),e},u.extend=function(e){return h(s.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},u.keys=a||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)u.has(e,n)&&(t[t.length]=n);return t},u.bind=function(e,t){if(e.bind===l&&l)return l.apply(e,s.call(arguments,1));var n=s.call(arguments,2);return function(){return e.apply(t,n.concat(s.call(arguments)))}},u.isObject=function(e){return e===Object(e)},u}(),window.Messenger.Events=function(){if(window.Backbone&&Backbone.Events)return Backbone.Events;var e=function(){var e=/\s+/,t=function(t,n,s,r){if(!s)return!0;if("object"==typeof s)for(var o in s)t[n].apply(t,[o,s[o]].concat(r));else{if(!e.test(s))return!0;for(var i=s.split(e),a=0,l=i.length;l>a;a++)t[n].apply(t,[i[a]].concat(r))}},n=function(e,t){var n,s=-1,r=e.length;switch(t.length){case 0:for(;r>++s;)(n=e[s]).callback.call(n.ctx);return;case 1:for(;r>++s;)(n=e[s]).callback.call(n.ctx,t[0]);return;case 2:for(;r>++s;)(n=e[s]).callback.call(n.ctx,t[0],t[1]);return;case 3:for(;r>++s;)(n=e[s]).callback.call(n.ctx,t[0],t[1],t[2]);return;default:for(;r>++s;)(n=e[s]).callback.apply(n.ctx,t)}},s={on:function(e,n,s){if(!t(this,"on",e,[n,s])||!n)return this;this._events||(this._events={});var r=this._events[e]||(this._events[e]=[]);return r.push({callback:n,context:s,ctx:s||this}),this},once:function(e,n,s){if(!t(this,"once",e,[n,s])||!n)return this;var r=this,o=_.once(function(){r.off(e,o),n.apply(this,arguments)});return o._callback=n,this.on(e,o,s),this},off:function(e,n,s){var r,o,i,a,l,u,c,h;if(!this._events||!t(this,"off",e,[n,s]))return this;if(!e&&!n&&!s)return this._events={},this;for(a=e?[e]:_.keys(this._events),l=0,u=a.length;u>l;l++)if(e=a[l],r=this._events[e]){if(i=[],n||s)for(c=0,h=r.length;h>c;c++)o=r[c],(n&&n!==o.callback&&n!==o.callback._callback||s&&s!==o.context)&&i.push(o);this._events[e]=i}return this},trigger:function(e){if(!this._events)return this;var s=Array.prototype.slice.call(arguments,1);if(!t(this,"trigger",e,s))return this;var r=this._events[e],o=this._events.all;return r&&n(r,s),o&&n(o,arguments),this},listenTo:function(e,t,n){var s=this._listeners||(this._listeners={}),r=e._listenerId||(e._listenerId=_.uniqueId("l"));return s[r]=e,e.on(t,"object"==typeof t?this:n,this),this},stopListening:function(e,t,n){var s=this._listeners;if(s){if(e)e.off(t,"object"==typeof t?this:n,this),t||n||delete s[e._listenerId];else{"object"==typeof t&&(n=this);for(var r in s)s[r].off(t,n,this);this._listeners={}}return this}}};return s.bind=s.on,s.unbind=s.off,s};return e()}(),function(){var e,t,n,s,r,o,i,a,l,u,c,h={}.hasOwnProperty,p=function(e,t){function n(){this.constructor=e}for(var s in t)h.call(t,s)&&(e[s]=t[s]);return n.prototype=t.prototype,e.prototype=new n,e.__super__=t.prototype,e},d=[].slice,f=[].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1};e=jQuery,o=null!=(l=window._)?l:window.Messenger._,s=null!=(u="undefined"!=typeof Backbone&&null!==Backbone?Backbone.Events:void 0)?u:window.Messenger.Events,n=function(){function t(t){e.extend(this,s),o.isObject(t)&&(t.el&&this.setElement(t.el),this.model=t.model),this.initialize.apply(this,arguments)}return t.prototype.setElement=function(t){return this.$el=e(t),this.el=this.$el[0]},t.prototype.delegateEvents=function(e){var t,n,s,r,i,a,l;if(e||(e=o.result(this,"events"))){this.undelegateEvents(),t=/^(\S+)\s*(.*)$/,l=[];for(s in e){if(i=e[s],o.isFunction(i)||(i=this[e[s]]),!i)throw Error('Method "'+e[s]+'" does not exist');r=s.match(t),n=r[1],a=r[2],i=o.bind(i,this),n+=".delegateEvents"+this.cid,""===a?l.push(this.jqon(n,i)):l.push(this.jqon(n,a,i))}return l}},t.prototype.jqon=function(e,t,n){var s;return null!=this.$el.on?(s=this.$el).on.apply(s,arguments):(null==n&&(n=t,t=void 0),null!=t?this.$el.delegate(t,e,n):this.$el.bind(e,n))},t.prototype.jqoff=function(e){var t;return null!=this.$el.off?(t=this.$el).off.apply(t,arguments):(this.$el.undelegate(),this.$el.unbind(e))},t.prototype.undelegateEvents=function(){return this.jqoff(".delegateEvents"+this.cid)},t.prototype.remove=function(){return this.undelegateEvents(),this.$el.remove()},t}(),i=function(t){function n(){return n.__super__.constructor.apply(this,arguments)}return p(n,t),n.prototype.defaults={hideAfter:10,scroll:!0,closeButtonText:"×"},n.prototype.initialize=function(t){return null==t&&(t={}),this.shown=!1,this.rendered=!1,this.messenger=t.messenger,this.options=e.extend({},this.options,t,this.defaults)},n.prototype.show=function(){var e;return this.rendered||this.render(),this.$message.removeClass("messenger-hidden"),e=this.shown,this.shown=!0,e?void 0:this.trigger("show")},n.prototype.hide=function(){var e;if(this.rendered)return this.$message.addClass("messenger-hidden"),e=this.shown,this.shown=!1,e?this.trigger("hide"):void 0},n.prototype.cancel=function(){return this.hide()},n.prototype.update=function(t){var n,s=this;return o.isString(t)&&(t={message:t}),e.extend(this.options,t),this.lastUpdate=new Date,this.rendered=!1,this.events=null!=(n=this.options.events)?n:{},this.render(),this.actionsToEvents(),this.delegateEvents(),this.checkClickable(),this.options.hideAfter?(this.$message.addClass("messenger-will-hide-after"),null!=this._hideTimeout&&clearTimeout(this._hideTimeout),this._hideTimeout=setTimeout(function(){return s.hide()},1e3*this.options.hideAfter)):this.$message.removeClass("messenger-will-hide-after"),this.options.hideOnNavigate?(this.$message.addClass("messenger-will-hide-on-navigate"),null!=("undefined"!=typeof Backbone&&null!==Backbone?Backbone.history:void 0)&&Backbone.history.on("route",function(){return s.hide()})):this.$message.removeClass("messenger-will-hide-on-navigate"),this.trigger("update",this)},n.prototype.scrollTo=function(){return this.options.scroll?e.scrollTo(this.$el,{duration:400,offset:{left:0,top:-20}}):void 0},n.prototype.timeSinceUpdate=function(){return this.lastUpdate?new Date-this.lastUpdate:null},n.prototype.actionsToEvents=function(){var e,t,n,s,r=this;n=this.options.actions,s=[];for(t in n)e=n[t],s.push(this.events['click [data-action="'+t+'"] a']=function(e){return function(n){return n.preventDefault(),n.stopPropagation(),r.trigger("action:"+t,e,n),e.action.call(r,n,r)}}(e));return s},n.prototype.checkClickable=function(){var e,t,n,s;n=this.events,s=[];for(t in n)e=n[t],"click"===t?s.push(this.$message.addClass("messenger-clickable")):s.push(void 0);return s},n.prototype.undelegateEvents=function(){var e;return n.__super__.undelegateEvents.apply(this,arguments),null!=(e=this.$message)?e.removeClass("messenger-clickable"):void 0},n.prototype.parseActions=function(){var t,n,s,r,o,i;n=[],o=this.options.actions;for(r in o)t=o[r],s=e.extend({},t),s.name=r,null==(i=s.label)&&(s.label=r),n.push(s);return n},n.prototype.template=function(t){var n,s,r,o,i,a,l,u,c,h,p=this;for(i=e(""),t.showCloseButton&&(r=e('
'),r.html(t.closeButtonText),r.click(function(){return p.cancel(),!0}),i.append(r)),a=e(''+t.message+"
"),i.append(a),t.actions.length&&(s=e('')),h=t.actions,u=0,c=h.length;c>u;u++)l=h[u],n=e("
"),n.attr("data-action",""+l.name),o=e(""),o.html(l.label),n.append(e('')),n.append(o),s.append(n);return i.append(s),i},n.prototype.render=function(){var t;if(!this.rendered)return this._hasSlot||(this.setElement(this.messenger._reserveMessageSlot(this)),this._hasSlot=!0),t=e.extend({},this.options,{actions:this.parseActions()}),this.$message=e(this.template(t)),this.$el.html(this.$message),this.shown=!0,this.rendered=!0,this.trigger("render")},n}(n),r=function(e){function t(){return t.__super__.constructor.apply(this,arguments)}return p(t,e),t.prototype.initialize=function(){return t.__super__.initialize.apply(this,arguments),this._timers={}},t.prototype.cancel=function(){return this.clearTimers(),this.hide(),null!=this._actionInstance&&null!=this._actionInstance.abort?this._actionInstance.abort():void 0},t.prototype.clearTimers=function(){var e,t,n,s;n=this._timers;for(e in n)t=n[e],clearTimeout(t);return this._timers={},null!=(s=this.$message)?s.removeClass("messenger-retry-soon messenger-retry-later"):void 0},t.prototype.render=function(){var e,n,s,r;t.__super__.render.apply(this,arguments),this.clearTimers(),s=this.options.actions,r=[];for(n in s)e=s[n],e.auto?r.push(this.startCountdown(n,e)):r.push(void 0);return r},t.prototype.renderPhrase=function(e,t){var n;return n=e.phrase.replace("TIME",this.formatTime(t))},t.prototype.formatTime=function(e){var t;return t=function(e,t){return e=Math.floor(e),1!==e&&(t+="s"),"in "+e+" "+t},0===Math.floor(e)?"now...":60>e?t(e,"second"):(e/=60,60>e?t(e,"minute"):(e/=60,t(e,"hour")))},t.prototype.startCountdown=function(e,t){var n,s,r,o,i=this;if(null==this._timers[e])return n=this.$message.find("[data-action='"+e+"'] .messenger-phrase"),s=null!=(o=t.delay)?o:3,10>=s?(this.$message.removeClass("messenger-retry-later"),this.$message.addClass("messenger-retry-soon")):(this.$message.removeClass("messenger-retry-soon"),this.$message.addClass("messenger-retry-later")),r=function(){var o;return n.text(i.renderPhrase(t,s)),s>0?(o=Math.min(s,1),s-=o,i._timers[e]=setTimeout(r,1e3*o)):(i.$message.removeClass("messenger-retry-soon messenger-retry-later"),delete i._timers[e],t.action())},r()},t}(i),a=function(t){function n(){return n.__super__.constructor.apply(this,arguments)}return p(n,t),n.prototype.tagName="ul",n.prototype.className="messenger",n.prototype.messageDefaults={type:"info"},n.prototype.initialize=function(t){return this.options=null!=t?t:{},this.history=[],this.messageDefaults=e.extend({},this.messageDefaults,this.options.messageDefaults)},n.prototype.render=function(){return this.updateMessageSlotClasses()},n.prototype.findById=function(e){return o.filter(this.history,function(t){return t.msg.options.id===e})},n.prototype._reserveMessageSlot=function(t){var n,s,r=this;for(n=e(""),n.addClass("messenger-message-slot"),this.$el.prepend(n),this.history.push({msg:t,$slot:n}),this._enforceIdConstraint(t),t.on("update",function(){return r._enforceIdConstraint(t)});this.options.maxMessages&&this.history.length>this.options.maxMessages;)s=this.history.shift(),s.msg.remove(),s.$slot.remove();return n},n.prototype._enforceIdConstraint=function(e){var t,n,s,r,o;if(null!=e.options.id)for(o=this.history,n=0,s=o.length;s>n;n++)if(t=o[n],r=t.msg,null!=r.options.id&&r.options.id===e.options.id&&e!==r){if(e.options.singleton)return e.hide(),void 0;r.hide()}},n.prototype.newMessage=function(e){var t,n,s,o,a=this;return null==e&&(e={}),e.messenger=this,i=null!=(n=null!=(s=Messenger.themes[null!=(o=e.theme)?o:this.options.theme])?s.Message:void 0)?n:r,t=new i(e),t.on("show",function(){return e.scrollTo&&"fixed"!==a.$el.css("position")?t.scrollTo():void 0}),t.on("hide show render",this.updateMessageSlotClasses,this),t},n.prototype.updateMessageSlotClasses=function(){var e,t,n,s,r,o,i;for(s=!0,t=null,e=!1,i=this.history,r=0,o=i.length;o>r;r++)n=i[r],n.$slot.removeClass("messenger-first messenger-last messenger-shown"),n.msg.shown&&n.msg.rendered&&(n.$slot.addClass("messenger-shown"),e=!0,t=n,s&&(s=!1,n.$slot.addClass("messenger-first")));return null!=t&&t.$slot.addClass("messenger-last"),this.$el[""+(e?"remove":"add")+"Class"]("messenger-empty")},n.prototype.hideAll=function(){var e,t,n,s,r;for(s=this.history,r=[],t=0,n=s.length;n>t;t++)e=s[t],r.push(e.msg.hide());return r},n.prototype.post=function(t){var n;return o.isString(t)&&(t={message:t}),t=e.extend(!0,{},this.messageDefaults,t),n=this.newMessage(t),n.update(t),n},n}(n),t=function(t){function n(){return n.__super__.constructor.apply(this,arguments)}return p(n,t),n.prototype.doDefaults={progressMessage:null,successMessage:null,errorMessage:"Error connecting to the server.",showSuccessWithoutError:!0,retry:{auto:!0,allow:!0},action:e.ajax},n.prototype.hookBackboneAjax=function(t){var n,s=this;if(null==t&&(t={}),null==window.Backbone)throw"Expected Backbone to be defined";return t=o.defaults(t,{id:"BACKBONE_ACTION",errorMessage:!1,successMessage:"Request completed successfully.",showSuccessWithoutError:!1}),n=function(e){var n;return n=o.extend({},t,e.messenger),s["do"](n,e)},null!=Backbone.ajax?(Backbone.ajax._withoutMessenger&&(Backbone.ajax=Backbone.ajax._withoutMessenger),(null==t.action||t.action===this.doDefaults.action)&&(t.action=Backbone.ajax),n._withoutMessenger=Backbone.ajax,Backbone.ajax=n):Backbone.sync=o.wrap(Backbone.sync,function(){var t,s,r;return r=arguments[0],t=arguments.length>=2?d.call(arguments,1):[],s=e.ajax,e.ajax=n,r.call.apply(r,[this].concat(d.call(t))),e.ajax=s})},n.prototype._getHandlerResponse=function(e){return e===!1?!1:e===!0||null==e?!0:e},n.prototype._parseEvents=function(e){var t,n,s,r,o,i,a;null==e&&(e={}),o={};for(r in e)s=e[r],n=r.indexOf(" "),i=r.substring(0,n),t=r.substring(n+1),null==(a=o[i])&&(o[i]={}),o[i][t]=s;return o},n.prototype._normalizeResponse=function(){var e,t,n,s,r,i,a;for(n=arguments.length>=1?d.call(arguments,0):[],s=null,r=null,e=null,i=0,a=n.length;a>i;i++)t=n[i],"success"===t||"timeout"===t||"abort"===t?s=t:null!=(null!=t?t.readyState:void 0)&&null!=(null!=t?t.responseText:void 0)?r=t:o.isObject(t)&&(e=t);return[s,e,r]},n.prototype.run=function(){var t,n,s,r,i,a,l,u,c,h,p,g=this;if(a=arguments[0],c=arguments[1],t=arguments.length>=3?d.call(arguments,2):[],null==c&&(c={}),a=e.extend(!0,{},this.messageDefaults,this.doDefaults,null!=a?a:{}),n=this._parseEvents(a.events),s=function(e,t){var n;return n=a[e+"Message"],o.isFunction(n)?n.call(g,e,t):n},l=null!=(p=a.messageInstance)?p:this.newMessage(a),null!=a.id&&(l.options.id=a.id),null!=a.progressMessage&&l.update(e.extend({},a,{message:s("progress",null),type:"info"})),i={},o.each(["error","success"],function(r){var u;return u=c[r],i[r]=function(){var i,h,p,m,y,v,_,w,b,x,M,C,k,$,E;return v=arguments.length>=1?d.call(arguments,0):[],b=g._normalizeResponse.apply(g,v),y=b[0],i=b[1],w=b[2],"success"===r&&null==l.errorCount&&a.showSuccessWithoutError===!1&&(a.successMessage=null),"error"===r&&(null==(x=a.errorCount)&&(a.errorCount=0),a.errorCount+=1),p=a.returnsPromise?v[0]:"function"==typeof u?u.apply(null,v):void 0,_=g._getHandlerResponse(p),o.isString(_)&&(_={message:_}),"error"!==r||0!==(null!=w?w.status:void 0)&&"abort"!==y?"error"===r&&null!=a.ignoredErrorCodes&&(M=null!=w?w.status:void 0,f.call(a.ignoredErrorCodes,M)>=0)?(l.hide(),void 0):(h={message:s(r,w),type:r,events:null!=(C=n[r])?C:{},hideOnNavigate:"success"===r},m=e.extend({},a,h,_),"number"==typeof(null!=(k=m.retry)?k.allow:void 0)&&m.retry.allow--,"error"===r&&(null!=w?w.status:void 0)>=500&&(null!=($=m.retry)?$.allow:void 0)?(null==m.retry.delay&&(m.retry.delay=4>m.errorCount?10:300),m.hideAfter&&(null==(E=m._hideAfter)&&(m._hideAfter=m.hideAfter),m.hideAfter=m._hideAfter+m.retry.delay),m._retryActions=!0,m.actions={retry:{label:"retry now",phrase:"Retrying TIME",auto:m.retry.auto,delay:m.retry.delay,action:function(){return m.messageInstance=l,setTimeout(function(){return g["do"].apply(g,[m,c].concat(d.call(t)))},0)}},cancel:{action:function(){return l.cancel()}}}):m._retryActions&&(delete m.actions.retry,delete m.actions.cancel,delete a._retryActions),l.update(m),_&&m.message?(Messenger(o.extend({},g.options,{instance:g})),l.show()):l.hide()):(l.hide(),void 0)}}),!a.returnsPromise)for(h in i)r=i[h],u=c[h],c[h]=r;return l._actionInstance=a.action.apply(a,[c].concat(d.call(t))),a.returnsPromise&&l._actionInstance.then(i.success,i.error),l},n.prototype["do"]=n.prototype.run,n.prototype.ajax=function(){var t,n;return n=arguments[0],t=arguments.length>=2?d.call(arguments,1):[],n.action=e.ajax,this.run.apply(this,[n].concat(d.call(t)))},n.prototype.expectPromise=function(e,t){return t=o.extend({},t,{action:e,returnsPromise:!0}),this.run(t)},n.prototype.error=function(e){return null==e&&(e={}),"string"==typeof e&&(e={message:e}),e.type="error",this.post(e)},n.prototype.info=function(e){return null==e&&(e={}),"string"==typeof e&&(e={message:e}),e.type="info",this.post(e)},n.prototype.success=function(e){return null==e&&(e={}),"string"==typeof e&&(e={message:e}),e.type="success",this.post(e)},n}(a),e.fn.messenger=function(){var n,s,r,i,l,u,c,h;return r=arguments[0],s=arguments.length>=2?d.call(arguments,1):[],null==r&&(r={}),n=this,null!=r&&o.isString(r)?(h=n.data("messenger"))[r].apply(h,s):(l=r,null==n.data("messenger")&&(a=null!=(u=null!=(c=Messenger.themes[l.theme])?c.Messenger:void 0)?u:t,n.data("messenger",i=new a(e.extend({el:n},l))),i.render()),n.data("messenger"))},window.Messenger._call=function(t){var n,s,r,o,i,a,l,u,c,h,p;if(a={extraClasses:"messenger-fixed messenger-on-bottom messenger-on-right",theme:"future",maxMessages:9,parentLocations:["body"]},t=e.extend(a,e._messengerDefaults,Messenger.options,t),null!=t.theme&&(t.extraClasses+=" messenger-theme-"+t.theme),l=t.instance||Messenger.instance,null==t.instance){for(c=t.parentLocations,s=null,r=null,h=0,p=c.length;p>h;h++)if(u=c[h],s=e(u),s.length){o=u;break}l?e(l._location).is(e(o))||(l.$el.detach(),s.prepend(l.$el)):(n=e(""),s.prepend(n),l=n.messenger(t),l._location=o,Messenger.instance=l)}return null!=l._addedClasses&&l.$el.removeClass(l._addedClasses),l.$el.addClass(i=""+l.className+" "+t.extraClasses),l._addedClasses=i,l},e.extend(Messenger,{Message:r,Messenger:t,themes:null!=(c=Messenger.themes)?c:{}}),e.globalMessenger=window.Messenger=Messenger}.call(this);
--------------------------------------------------------------------------------
/github2fedmsg/static/messenger-1.4.1/css/messenger-theme-future.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes ui-spinner-rotate-right {
2 | /* line 64, ../../src/sass/messenger-spinner.scss */
3 | 0% {
4 | -webkit-transform: rotate(0deg);
5 | }
6 |
7 | /* line 65, ../../src/sass/messenger-spinner.scss */
8 | 25% {
9 | -webkit-transform: rotate(180deg);
10 | }
11 |
12 | /* line 66, ../../src/sass/messenger-spinner.scss */
13 | 50% {
14 | -webkit-transform: rotate(180deg);
15 | }
16 |
17 | /* line 67, ../../src/sass/messenger-spinner.scss */
18 | 75% {
19 | -webkit-transform: rotate(360deg);
20 | }
21 |
22 | /* line 68, ../../src/sass/messenger-spinner.scss */
23 | 100% {
24 | -webkit-transform: rotate(360deg);
25 | }
26 | }
27 |
28 | @-webkit-keyframes ui-spinner-rotate-left {
29 | /* line 72, ../../src/sass/messenger-spinner.scss */
30 | 0% {
31 | -webkit-transform: rotate(0deg);
32 | }
33 |
34 | /* line 73, ../../src/sass/messenger-spinner.scss */
35 | 25% {
36 | -webkit-transform: rotate(0deg);
37 | }
38 |
39 | /* line 74, ../../src/sass/messenger-spinner.scss */
40 | 50% {
41 | -webkit-transform: rotate(180deg);
42 | }
43 |
44 | /* line 75, ../../src/sass/messenger-spinner.scss */
45 | 75% {
46 | -webkit-transform: rotate(180deg);
47 | }
48 |
49 | /* line 76, ../../src/sass/messenger-spinner.scss */
50 | 100% {
51 | -webkit-transform: rotate(360deg);
52 | }
53 | }
54 |
55 | @-moz-keyframes ui-spinner-rotate-right {
56 | /* line 80, ../../src/sass/messenger-spinner.scss */
57 | 0% {
58 | -moz-transform: rotate(0deg);
59 | }
60 |
61 | /* line 81, ../../src/sass/messenger-spinner.scss */
62 | 25% {
63 | -moz-transform: rotate(180deg);
64 | }
65 |
66 | /* line 82, ../../src/sass/messenger-spinner.scss */
67 | 50% {
68 | -moz-transform: rotate(180deg);
69 | }
70 |
71 | /* line 83, ../../src/sass/messenger-spinner.scss */
72 | 75% {
73 | -moz-transform: rotate(360deg);
74 | }
75 |
76 | /* line 84, ../../src/sass/messenger-spinner.scss */
77 | 100% {
78 | -moz-transform: rotate(360deg);
79 | }
80 | }
81 |
82 | @-moz-keyframes ui-spinner-rotate-left {
83 | /* line 88, ../../src/sass/messenger-spinner.scss */
84 | 0% {
85 | -moz-transform: rotate(0deg);
86 | }
87 |
88 | /* line 89, ../../src/sass/messenger-spinner.scss */
89 | 25% {
90 | -moz-transform: rotate(0deg);
91 | }
92 |
93 | /* line 90, ../../src/sass/messenger-spinner.scss */
94 | 50% {
95 | -moz-transform: rotate(180deg);
96 | }
97 |
98 | /* line 91, ../../src/sass/messenger-spinner.scss */
99 | 75% {
100 | -moz-transform: rotate(180deg);
101 | }
102 |
103 | /* line 92, ../../src/sass/messenger-spinner.scss */
104 | 100% {
105 | -moz-transform: rotate(360deg);
106 | }
107 | }
108 |
109 | @keyframes ui-spinner-rotate-right {
110 | /* line 96, ../../src/sass/messenger-spinner.scss */
111 | 0% {
112 | transform: rotate(0deg);
113 | }
114 |
115 | /* line 97, ../../src/sass/messenger-spinner.scss */
116 | 25% {
117 | transform: rotate(180deg);
118 | }
119 |
120 | /* line 98, ../../src/sass/messenger-spinner.scss */
121 | 50% {
122 | transform: rotate(180deg);
123 | }
124 |
125 | /* line 99, ../../src/sass/messenger-spinner.scss */
126 | 75% {
127 | transform: rotate(360deg);
128 | }
129 |
130 | /* line 100, ../../src/sass/messenger-spinner.scss */
131 | 100% {
132 | transform: rotate(360deg);
133 | }
134 | }
135 |
136 | @keyframes ui-spinner-rotate-left {
137 | /* line 104, ../../src/sass/messenger-spinner.scss */
138 | 0% {
139 | transform: rotate(0deg);
140 | }
141 |
142 | /* line 105, ../../src/sass/messenger-spinner.scss */
143 | 25% {
144 | transform: rotate(0deg);
145 | }
146 |
147 | /* line 106, ../../src/sass/messenger-spinner.scss */
148 | 50% {
149 | transform: rotate(180deg);
150 | }
151 |
152 | /* line 107, ../../src/sass/messenger-spinner.scss */
153 | 75% {
154 | transform: rotate(180deg);
155 | }
156 |
157 | /* line 108, ../../src/sass/messenger-spinner.scss */
158 | 100% {
159 | transform: rotate(360deg);
160 | }
161 | }
162 |
163 | /* line 116, ../../src/sass/messenger-spinner.scss */
164 | .messenger-spinner {
165 | position: relative;
166 | border-radius: 100%;
167 | }
168 | /* line 120, ../../src/sass/messenger-spinner.scss */
169 | ul.messenger.messenger-spinner-active .messenger-spinner .messenger-spinner {
170 | display: block;
171 | }
172 | /* line 124, ../../src/sass/messenger-spinner.scss */
173 | .messenger-spinner .messenger-spinner-side {
174 | width: 50%;
175 | height: 100%;
176 | overflow: hidden;
177 | position: absolute;
178 | }
179 | /* line 130, ../../src/sass/messenger-spinner.scss */
180 | .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
181 | border-radius: 999px;
182 | position: absolute;
183 | width: 100%;
184 | height: 100%;
185 | -webkit-animation-iteration-count: infinite;
186 | -moz-animation-iteration-count: infinite;
187 | -ms-animation-iteration-count: infinite;
188 | -o-animation-iteration-count: infinite;
189 | animation-iteration-count: infinite;
190 | -webkit-animation-timing-function: linear;
191 | -moz-animation-timing-function: linear;
192 | -ms-animation-timing-function: linear;
193 | -o-animation-timing-function: linear;
194 | animation-timing-function: linear;
195 | }
196 | /* line 140, ../../src/sass/messenger-spinner.scss */
197 | .messenger-spinner .messenger-spinner-side-left {
198 | left: 0;
199 | }
200 | /* line 143, ../../src/sass/messenger-spinner.scss */
201 | .messenger-spinner .messenger-spinner-side-left .messenger-spinner-fill {
202 | left: 100%;
203 | border-top-left-radius: 0;
204 | border-bottom-left-radius: 0;
205 | -webkit-animation-name: ui-spinner-rotate-left;
206 | -moz-animation-name: ui-spinner-rotate-left;
207 | -ms-animation-name: ui-spinner-rotate-left;
208 | -o-animation-name: ui-spinner-rotate-left;
209 | animation-name: ui-spinner-rotate-left;
210 | -webkit-transform-origin: 0 50%;
211 | -moz-transform-origin: 0 50%;
212 | -ms-transform-origin: 0 50%;
213 | -o-transform-origin: 0 50%;
214 | transform-origin: 0 50%;
215 | }
216 | /* line 152, ../../src/sass/messenger-spinner.scss */
217 | .messenger-spinner .messenger-spinner-side-right {
218 | left: 50%;
219 | }
220 | /* line 155, ../../src/sass/messenger-spinner.scss */
221 | .messenger-spinner .messenger-spinner-side-right .messenger-spinner-fill {
222 | left: -100%;
223 | border-top-right-radius: 0;
224 | border-bottom-right-radius: 0;
225 | -webkit-animation-name: ui-spinner-rotate-right;
226 | -moz-animation-name: ui-spinner-rotate-right;
227 | -ms-animation-name: ui-spinner-rotate-right;
228 | -o-animation-name: ui-spinner-rotate-right;
229 | animation-name: ui-spinner-rotate-right;
230 | -webkit-transform-origin: 100% 50%;
231 | -moz-transform-origin: 100% 50%;
232 | -ms-transform-origin: 100% 50%;
233 | -o-transform-origin: 100% 50%;
234 | transform-origin: 100% 50%;
235 | }
236 |
237 | /* line 15, ../../src/sass/messenger-theme-future.sass */
238 | ul.messenger-theme-future {
239 | -webkit-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.24), 0px 1px 5px rgba(0, 0, 0, 0.6);
240 | -moz-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.24), 0px 1px 5px rgba(0, 0, 0, 0.6);
241 | box-shadow: inset 0px 1px rgba(255, 255, 255, 0.24), 0px 1px 5px rgba(0, 0, 0, 0.6);
242 | -webkit-border-radius: 4px;
243 | -moz-border-radius: 4px;
244 | -ms-border-radius: 4px;
245 | -o-border-radius: 4px;
246 | border-radius: 4px;
247 | -moz-user-select: none;
248 | -webkit-user-select: none;
249 | -o-user-select: none;
250 | user-select: none;
251 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5c5b5b), color-stop(100%, #353535));
252 | background-image: -webkit-linear-gradient(#5c5b5b, #353535);
253 | background-image: -moz-linear-gradient(#5c5b5b, #353535);
254 | background-image: -o-linear-gradient(#5c5b5b, #353535);
255 | background-image: linear-gradient(#5c5b5b, #353535);
256 | background-color: #5c5b5b;
257 | border: 1px solid rgba(0, 0, 0, 0.5);
258 | }
259 | /* line 23, ../../src/sass/messenger-theme-future.sass */
260 | ul.messenger-theme-future .messenger-message {
261 | -webkit-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 0px -1px rgba(0, 0, 0, 0.23), inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
262 | -moz-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 0px -1px rgba(0, 0, 0, 0.23), inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
263 | box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 0px -1px rgba(0, 0, 0, 0.23), inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
264 | -webkit-border-radius: 0px;
265 | -moz-border-radius: 0px;
266 | -ms-border-radius: 0px;
267 | -o-border-radius: 0px;
268 | border-radius: 0px;
269 | position: relative;
270 | border: 0px;
271 | margin-bottom: 0px;
272 | font-size: 13px;
273 | background: transparent;
274 | color: #f0f0f0;
275 | text-shadow: 0px 1px #111111;
276 | font-weight: 500;
277 | padding: 10px 30px 13px 65px;
278 | }
279 | /* line 36, ../../src/sass/messenger-theme-future.sass */
280 | ul.messenger-theme-future .messenger-message a {
281 | color: #5599ff;
282 | }
283 | /* line 39, ../../src/sass/messenger-theme-future.sass */
284 | ul.messenger-theme-future .messenger-message .messenger-close {
285 | position: absolute;
286 | top: 0px;
287 | right: 0px;
288 | color: #888888;
289 | text-shadow: 0px 1px black;
290 | opacity: 1;
291 | font-weight: bold;
292 | display: block;
293 | font-size: 20px;
294 | line-height: 20px;
295 | padding: 8px 10px 7px 7px;
296 | cursor: pointer;
297 | background: transparent;
298 | border: 0;
299 | -webkit-appearance: none;
300 | }
301 | /* line 56, ../../src/sass/messenger-theme-future.sass */
302 | ul.messenger-theme-future .messenger-message .messenger-close:hover {
303 | color: #bbbbbb;
304 | }
305 | /* line 59, ../../src/sass/messenger-theme-future.sass */
306 | ul.messenger-theme-future .messenger-message .messenger-close:active {
307 | color: #777777;
308 | }
309 | /* line 62, ../../src/sass/messenger-theme-future.sass */
310 | ul.messenger-theme-future .messenger-message .messenger-actions {
311 | float: none;
312 | margin-top: 10px;
313 | }
314 | /* line 66, ../../src/sass/messenger-theme-future.sass */
315 | ul.messenger-theme-future .messenger-message .messenger-actions a {
316 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), inset 0px 1px rgba(255, 255, 255, 0.1);
317 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), inset 0px 1px rgba(255, 255, 255, 0.1);
318 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), inset 0px 1px rgba(255, 255, 255, 0.1);
319 | -webkit-border-radius: 4px;
320 | -moz-border-radius: 4px;
321 | -ms-border-radius: 4px;
322 | -o-border-radius: 4px;
323 | border-radius: 4px;
324 | text-decoration: none;
325 | display: inline-block;
326 | padding: 10px;
327 | color: #aaaaaa;
328 | text-shadow: 0px 1px #222222;
329 | margin-right: 10px;
330 | padding: 3px 10px 5px;
331 | text-transform: capitalize;
332 | }
333 | /* line 78, ../../src/sass/messenger-theme-future.sass */
334 | ul.messenger-theme-future .messenger-message .messenger-actions a:hover {
335 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), inset 0px 1px rgba(255, 255, 255, 0.2);
336 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), inset 0px 1px rgba(255, 255, 255, 0.2);
337 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), inset 0px 1px rgba(255, 255, 255, 0.2);
338 | color: #f0f0f0;
339 | }
340 | /* line 82, ../../src/sass/messenger-theme-future.sass */
341 | ul.messenger-theme-future .messenger-message .messenger-actions a:active {
342 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.28), inset 0px 1px rgba(0, 0, 0, 0.1);
343 | -moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.28), inset 0px 1px rgba(0, 0, 0, 0.1);
344 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.28), inset 0px 1px rgba(0, 0, 0, 0.1);
345 | background: rgba(0, 0, 0, 0.04);
346 | color: #aaaaaa;
347 | }
348 | /* line 87, ../../src/sass/messenger-theme-future.sass */
349 | ul.messenger-theme-future .messenger-message .messenger-actions .messenger-phrase {
350 | display: none;
351 | }
352 | /* line 90, ../../src/sass/messenger-theme-future.sass */
353 | ul.messenger-theme-future .messenger-message .messenger-message-inner:before {
354 | -webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.6), 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
355 | -moz-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.6), 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
356 | box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.6), 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
357 | -webkit-border-radius: 50%;
358 | -moz-border-radius: 50%;
359 | -ms-border-radius: 50%;
360 | -o-border-radius: 50%;
361 | border-radius: 50%;
362 | position: absolute;
363 | left: 17px;
364 | display: block;
365 | content: " ";
366 | top: 50%;
367 | margin-top: -8px;
368 | height: 13px;
369 | width: 13px;
370 | z-index: 20;
371 | }
372 | /* line 103, ../../src/sass/messenger-theme-future.sass */
373 | ul.messenger-theme-future .messenger-message.alert-success .messenger-message-inner:before {
374 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5fca4a), color-stop(100%, #098d38));
375 | background-image: -webkit-linear-gradient(top, #5fca4a, #098d38);
376 | background-image: -moz-linear-gradient(top, #5fca4a, #098d38);
377 | background-image: -o-linear-gradient(top, #5fca4a, #098d38);
378 | background-image: linear-gradient(top, #5fca4a, #098d38);
379 | background-color: #5fca4a;
380 | }
381 | /* line 107, ../../src/sass/messenger-theme-future.sass */
382 | ul.messenger-theme-future .messenger-message.alert-info .messenger-message-inner:before {
383 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #61c4b8), color-stop(100%, #1992a3));
384 | background-image: -webkit-linear-gradient(top, #61c4b8, #1992a3);
385 | background-image: -moz-linear-gradient(top, #61c4b8, #1992a3);
386 | background-image: -o-linear-gradient(top, #61c4b8, #1992a3);
387 | background-image: linear-gradient(top, #61c4b8, #1992a3);
388 | background-color: #61c4b8;
389 | }
390 | /* line 113, ../../src/sass/messenger-theme-future.sass */
391 | ul.messenger-theme-future .messenger-message.alert-error .messenger-message-inner:before {
392 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dd6a45), color-stop(100%, #91361a));
393 | background-image: -webkit-linear-gradient(top, #dd6a45, #91361a);
394 | background-image: -moz-linear-gradient(top, #dd6a45, #91361a);
395 | background-image: -o-linear-gradient(top, #dd6a45, #91361a);
396 | background-image: linear-gradient(top, #dd6a45, #91361a);
397 | background-color: #dd6a45;
398 | }
399 | /* line 32, ../../src/sass/messenger-spinner.scss */
400 | ul.messenger-theme-future .messenger-message.alert-error.messenger-retry-soon .messenger-spinner {
401 | width: 32px;
402 | height: 32px;
403 | background: transparent;
404 | }
405 | /* line 37, ../../src/sass/messenger-spinner.scss */
406 | ul.messenger-theme-future .messenger-message.alert-error.messenger-retry-soon .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
407 | background: #dd6a45;
408 | -webkit-animation-duration: 20s;
409 | -moz-animation-duration: 20s;
410 | -ms-animation-duration: 20s;
411 | -o-animation-duration: 20s;
412 | animation-duration: 20s;
413 | opacity: 1;
414 | }
415 | /* line 45, ../../src/sass/messenger-spinner.scss */
416 | ul.messenger-theme-future .messenger-message.alert-error.messenger-retry-soon .messenger-spinner:after {
417 | content: "";
418 | background: #333333;
419 | position: absolute;
420 | width: 26px;
421 | height: 26px;
422 | border-radius: 50%;
423 | top: 3px;
424 | left: 3px;
425 | display: block;
426 | }
427 | /* line 32, ../../src/sass/messenger-spinner.scss */
428 | ul.messenger-theme-future .messenger-message.alert-error.messenger-retry-later .messenger-spinner {
429 | width: 32px;
430 | height: 32px;
431 | background: transparent;
432 | }
433 | /* line 37, ../../src/sass/messenger-spinner.scss */
434 | ul.messenger-theme-future .messenger-message.alert-error.messenger-retry-later .messenger-spinner .messenger-spinner-side .messenger-spinner-fill {
435 | background: #dd6a45;
436 | -webkit-animation-duration: 600s;
437 | -moz-animation-duration: 600s;
438 | -ms-animation-duration: 600s;
439 | -o-animation-duration: 600s;
440 | animation-duration: 600s;
441 | opacity: 1;
442 | }
443 | /* line 45, ../../src/sass/messenger-spinner.scss */
444 | ul.messenger-theme-future .messenger-message.alert-error.messenger-retry-later .messenger-spinner:after {
445 | content: "";
446 | background: #333333;
447 | position: absolute;
448 | width: 26px;
449 | height: 26px;
450 | border-radius: 50%;
451 | top: 3px;
452 | left: 3px;
453 | display: block;
454 | }
455 | /* line 125, ../../src/sass/messenger-theme-future.sass */
456 | ul.messenger-theme-future .messenger-message-slot.messenger-last .messenger-message {
457 | -webkit-border-radius: 4px 4px 0px 0px;
458 | -moz-border-radius: 4px 4px 0px 0px;
459 | -ms-border-radius: 4px 4px 0px 0px;
460 | -o-border-radius: 4px 4px 0px 0px;
461 | border-radius: 4px 4px 0px 0px;
462 | }
463 | /* line 128, ../../src/sass/messenger-theme-future.sass */
464 | ul.messenger-theme-future .messenger-message-slot.messenger-first .messenger-message {
465 | -webkit-border-radius: 0px 0px 4px 4px;
466 | -moz-border-radius: 0px 0px 4px 4px;
467 | -ms-border-radius: 0px 0px 4px 4px;
468 | -o-border-radius: 0px 0px 4px 4px;
469 | border-radius: 0px 0px 4px 4px;
470 | -webkit-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
471 | -moz-box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
472 | box-shadow: inset 0px 1px rgba(255, 255, 255, 0.13), inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
473 | }
474 | /* line 132, ../../src/sass/messenger-theme-future.sass */
475 | ul.messenger-theme-future .messenger-message-slot.messenger-first.messenger-last .messenger-message {
476 | -webkit-border-radius: 4px;
477 | -moz-border-radius: 4px;
478 | -ms-border-radius: 4px;
479 | -o-border-radius: 4px;
480 | border-radius: 4px;
481 | -webkit-box-shadow: inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
482 | -moz-box-shadow: inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
483 | box-shadow: inset 48px 0px 0px rgba(0, 0, 0, 0.3), inset 46px 0px 0px rgba(255, 255, 255, 0.07);
484 | }
485 | /* line 136, ../../src/sass/messenger-theme-future.sass */
486 | ul.messenger-theme-future .messenger-spinner {
487 | display: block;
488 | position: absolute;
489 | left: 7px;
490 | top: 50%;
491 | margin-top: -18px;
492 | z-index: 999;
493 | height: 32px;
494 | width: 32px;
495 | z-index: 10;
496 | }
497 |
--------------------------------------------------------------------------------