Skip to content

Commit 8d9bc7d

Browse files
committed
Boilerplate for Hasmail.
0 parents  commit 8d9bc7d

33 files changed

+488
-0
lines changed

.coveragerc

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[run]
2+
source = hasmail
3+
4+
[report]
5+
# Regexes for lines to exclude from consideration
6+
exclude_lines =
7+
# Have to re-enable the standard pragma
8+
pragma: no cover
9+
10+
# Don't complain about missing debug-only code
11+
def __repr__
12+
if self\.debug
13+
14+
# Don't complain about importerror handlers
15+
except ImportError
16+
17+
# Don't complain if tests don't hit defensive assertion code:
18+
raise AssertionError
19+
raise NotImplementedError
20+
21+
# Don't complain if non-runnable code isn't run:
22+
if 0:
23+
if False:
24+
if __name__ == .__main__.:

.gitignore

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.DS_Store
2+
*.pyc
3+
test.db
4+
*.wpr
5+
.project
6+
.pydevproject
7+
.settings
8+
*.egg-info
9+
.coverage
10+
docs/_build
11+
build/
12+
dist/
13+
.webassets-cache
14+
nosetests.xml
15+
.sass-cache
16+
.idea
17+
instance/settings.py
18+
instance/testing.py
19+
instance/production.py
20+
instance/development.py
21+
baseframe-packed.css
22+
baseframe-packed.js
23+
packed.css
24+
packed.js
25+
error.log

.travis.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
language: python
2+
python:
3+
- "2.7"
4+
- "pypy"
5+
install:
6+
- pip install -r requirements.txt --use-mirrors
7+
- pip install -r test_requirements.txt --use-mirrors
8+
script:
9+
- coverage run `which nosetests` hasmail tests
10+
after_success:
11+
- coveralls
12+
notifications:
13+
email: false
14+
irc: "irc.freenode.net#hasgeek-dev"

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
HasGeek Custom Mailer
2+
=====================
3+
4+
![Build status](https://secure.travis-ci.org/hasgeek/hasmail.png)
5+
[![Coverage Status](https://coveralls.io/repos/hasgeek/hasmail/badge.png?branch=master)](https://coveralls.io/r/hasgeek/hasmail?branch=master)
6+
7+
Mass mailer with mail merge and per-recipient mail customisation.

config.rb

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Require any additional compass plugins here.
2+
# Set this to the root of your project when deployed:
3+
http_path = "/"
4+
css_dir = "hasmail/static/css"
5+
sass_dir = "hasmail/static/sass"
6+
images_dir = "hasmail/static/img"
7+
javascripts_dir = "hasmail/static/js"
8+
line_comments = false
9+
# To enable relative paths to assets via compass helper functions. Uncomment:
10+
relative_assets = true

hasmail/__init__.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# The imports in this file are order-sensitive
4+
5+
from __future__ import absolute_import
6+
from flask import Flask
7+
from flask.ext.lastuser import Lastuser
8+
from flask.ext.lastuser.sqlalchemy import UserManager
9+
from baseframe import baseframe, assets, Version
10+
import coaster.app
11+
from ._version import __version__
12+
13+
version = Version(__version__)
14+
15+
# First, make an app
16+
17+
app = Flask(__name__, instance_relative_config=True)
18+
lastuser = Lastuser()
19+
20+
# Second, import the models and views
21+
22+
from . import models, views
23+
from .models import db
24+
25+
# Third, setup baseframe and assets
26+
27+
assets['hasmail.js'][version] = 'js/app.js'
28+
assets['hasmail.css'][version] = 'css/app.css'
29+
30+
31+
# Configure the app
32+
def init_for(env):
33+
coaster.app.init_app(app, env)
34+
db.init_app(app)
35+
db.app = app
36+
baseframe.init_app(app, requires=['baseframe-bs3', 'hasmail'])
37+
lastuser.init_app(app)
38+
lastuser.init_usermanager(UserManager(db, models.User))

hasmail/_version.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__all__ = ['__version__', '__version_info__']
2+
3+
__version__ = '0.1.0-dev'
4+
__version_info__ = tuple([int(num) if num.isdigit() else num for num in __version__.replace('-', '.').split('.')])

hasmail/forms/__init__.py

Whitespace-only changes.

hasmail/models/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from coaster.sqlalchemy import IdMixin, TimestampMixin, BaseMixin, BaseNameMixin
4+
from coaster.db import db
5+
6+
from .user import *

hasmail/models/user.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from flask.ext.lastuser.sqlalchemy import UserBase
4+
from . import db
5+
6+
__all__ = ['User']
7+
8+
9+
class User(UserBase, db.Model):
10+
__tablename__ = 'user'

hasmail/static/css/app.css

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#logo {
2+
text-indent: -119988px;
3+
overflow: hidden;
4+
text-align: left;
5+
background-image: url('../img/logo.png?1386841977');
6+
background-repeat: no-repeat;
7+
background-position: 50% 50%;
8+
width: 120px;
9+
height: 75px;
10+
}
11+
12+
#hg-networkbar.navbar .navbar-brand {
13+
padding: 0;
14+
display: block;
15+
text-indent: -119988px;
16+
overflow: hidden;
17+
text-align: left;
18+
background-image: url('../img/logo.png?1386841977');
19+
background-repeat: no-repeat;
20+
background-position: 50% 50%;
21+
width: 120px;
22+
height: 75px;
23+
height: 48px;
24+
background-position: left center;
25+
background-size: auto 100%;
26+
background-repeat: none;
27+
text-indent: -1000px;
28+
}
29+
30+
@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
31+
#logo {
32+
background-image: url('../img/[email protected]?1386841968');
33+
-webkit-background-size: 120px 75px;
34+
}
35+
}

hasmail/static/img/logo.png

5.26 KB
Loading

hasmail/static/img/[email protected]

9.15 KB
Loading

hasmail/static/js/app.js

Whitespace-only changes.

hasmail/static/sass/app.sass

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Insert app styles here
2+
3+
@import "compass"
4+
5+
#logo
6+
@include replace-text-with-dimensions('logo.png')
7+
8+
#hg-networkbar.navbar .navbar-brand
9+
padding: 0
10+
display: block
11+
@include replace-text-with-dimensions('logo.png')
12+
height: 48px
13+
background-position: left center
14+
background-size: auto 100%
15+
background-repeat: none
16+
text-indent: -1000px
17+
18+
@media only screen and (-webkit-min-device-pixel-ratio: 1.5)
19+
#logo
20+
background-image: image-url('[email protected]')
21+
-webkit-background-size: image-width('logo.png') image-height('logo.png')

hasmail/templates/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% extends "layout.html" %}
2+
{% block titletags -%}
3+
<title>{% block title %}{{ config['SITE_TITLE'] }}{% endblock %}</title>
4+
<meta name="DC.title" content="{{ config['SITE_TITLE'] }}"/>
5+
{%- endblock %}
6+
7+
{% block content %}
8+
<p>
9+
Write once, customize for specific recipients, send all. Easiest mail merge ever.
10+
</p>
11+
<p>
12+
<a href="#" class="btn btn-primary">Get started</a>
13+
</p>
14+
{% endblock %}

hasmail/templates/layout.html

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% extends "baseframe.html" -%}
2+
{% from "baseframe/components.html" import responsive_networkbar with context %}
3+
4+
{% block networkbar %}{{ responsive_networkbar(siteid=config['SITE_ID'], login=config['LASTUSER_CLIENT_ID'] and true or false) }}{% endblock %}
5+
6+
{% block baseheadline -%}
7+
{% block headline -%}
8+
<div class="page-header">
9+
<h1>{{ self.title()|e }}</h1>
10+
</div>
11+
{%- endblock %}
12+
{%- endblock %}
13+
14+
{% block basecontent -%}
15+
{% block content %}{% endblock %}
16+
{%- endblock %}

hasmail/views/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from . import index, login

hasmail/views/index.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from flask import render_template
4+
from .. import app
5+
6+
7+
@app.route('/')
8+
def index():
9+
return render_template('index.html')

hasmail/views/login.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from flask import redirect, flash, Markup, escape
4+
from coaster.views import get_next_url
5+
from baseframe import _
6+
from baseframe.forms import render_message
7+
8+
from .. import app, lastuser
9+
from ..models import db
10+
11+
12+
@app.route('/login')
13+
@lastuser.login_handler
14+
def login():
15+
return {'scope': 'id'}
16+
17+
18+
@app.route('/logout')
19+
@lastuser.logout_handler
20+
def logout():
21+
flash(u"You are now logged out", category='success')
22+
return get_next_url()
23+
24+
25+
@app.route('/login/redirect')
26+
@lastuser.auth_handler
27+
def lastuserauth():
28+
return redirect(get_next_url())
29+
30+
31+
@app.route('/login/notify', methods=['POST'])
32+
@lastuser.notification_handler
33+
def lastusernotify(user):
34+
# Perform operations here if required.
35+
# Warning: this *could* be a spoof call, so ignore all request data.
36+
# Only trust the 'user' parameter to this function.
37+
db.session.commit()
38+
39+
40+
@lastuser.auth_error_handler
41+
def lastuser_error(error, error_description=None, error_uri=None):
42+
if error == 'access_denied':
43+
flash("You denied the request to login", category='error')
44+
return redirect(get_next_url())
45+
return render_message(
46+
title="Error: {0}".format(error),
47+
message=Markup(
48+
"<p>{desc}</p><p>URI: {uri}</p>".format(
49+
desc=escape(error_description or ''), uri=escape(error_uri or _('NA')))
50+
)
51+
)

instance/settings-sample.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -*- coding: utf-8 -*-
2+
#: Site title
3+
SITE_TITLE = 'HasGeek App'
4+
#: Site id (for network bar)
5+
SITE_ID = ''
6+
#: Google Analytics code
7+
GA_CODE = ''
8+
#: Google site verification code (inserted as a meta tag)
9+
GOOGLE_SITE_VERIFICATION = ''
10+
#: Typekit code
11+
TYPEKIT_CODE = ''
12+
#: Database backend
13+
SQLALCHEMY_DATABASE_URI = 'sqlite:///test.db'
14+
#: Secret key
15+
SECRET_KEY = 'make this something random'
16+
#: Cache type
17+
CACHE_TYPE = 'redis'
18+
#: Timezone
19+
TIMEZONE = 'Asia/Kolkata'
20+
#: Lastuser server
21+
LASTUSER_SERVER = 'https://auth.hasgeek.com/'
22+
#: Lastuser client id
23+
LASTUSER_CLIENT_ID = ''
24+
#: Lastuser client secret
25+
LASTUSER_CLIENT_SECRET = ''
26+
#: Mail settings
27+
#: MAIL_FAIL_SILENTLY : default True
28+
#: MAIL_SERVER : default 'localhost'
29+
#: MAIL_PORT : default 25
30+
#: MAIL_USE_TLS : default False
31+
#: MAIL_USE_SSL : default False
32+
#: MAIL_USERNAME : default None
33+
#: MAIL_PASSWORD : default None
34+
#: MAIL_DEFAULT_SENDER : default None
35+
MAIL_FAIL_SILENTLY = False
36+
MAIL_SERVER = 'localhost'
37+
MAIL_DEFAULT_SENDER = 'HasGeek <[email protected]>'
38+
DEFAULT_MAIL_SENDER = MAIL_DEFAULT_SENDER # For compatibility with older Flask-Mail
39+
#: Logging: recipients of error emails
40+
ADMINS = []
41+
#: Log file
42+
LOGFILE = 'error.log'
43+
# redis settings for RQ
44+
REDIS_URL = 'redis://localhost:6379/0'

0 commit comments

Comments
 (0)