Changeset - 7cf7d1e162a4
[Not reviewed]
default
0 5 0
Dennis Fink - 3 years ago 2021-12-27 21:52:14
dennis.fink@c3l.lu
Make alot of things configurable
5 files changed with 85 insertions and 45 deletions:
0 comments (0 inline, 0 general)
c3l_membership/__init__.py
Show inline comments
 
import base64
 
import json
 
import os.path
 
import secrets
 

	
 
from flask import Flask
 
from flask_pure import Pure
 
from flask_qrcode import QRcode
 

	
 
config_file = os.path.abspath("config.json")
 
config_file = os.path.abspath("/etc/membership.json")
 

	
 
pure = Pure()
 
qrcode = QRcode()
 

	
 

	
 
def create_app():
 

	
 
    app = Flask(__name__)
 

	
 
    if not hasattr(app.config, "from_json"):
 

	
 
        def from_json(file, silent=True):
 
            try:
 
                with open(file, encoding="utf-8") as json_file:
 
                    obj = json.load(json_file)
 
            except IOError:
 
                if silent:
 
                    return False
 
                raise
 

	
 
            for key in obj:
 
                if key.isupper():
 
                    app.config[key] = obj[key]
 

	
 
            return True
 

	
 
        app.config.from_json = from_json
 

	
 
    try:
 
        app.config.from_json(config_file)
 
        app.config.from_file(config_file, load=json.load)
 
    except FileNotFoundError:
 
        pass
 

	
 
    app.config["PURECSS_RESPONSIVE_GRIDS"] = True
 
    app.config["PURECSS_USE_CDN"] = False
 
    app.config["PURECSS_USE_MINIFIED"] = True
 
    pure.init_app(app)
 

	
 
    qrcode.init_app(app)
 

	
 
    app.config["SECRET_KEY"] = "foobar"
 
    _default_secret_key = base64.b64encode(secrets.token_bytes()).decode("utf-8")
 
    app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY", _default_secret_key)
 

	
 
    app.config.setdefault("SUPPORTING_FEE", 40)
 
    app.config.setdefault("REGULAR_FEE", 120)
 

	
 
    app.config.setdefault("DIGICASH_ENABLED", True)
 
    app.config.setdefault("BITCOIN_ENABLED", True)
 
    app.config.setdefault("ETHEREUM_ENABLED", True)
 
    app.config.setdefault("MONERO_ENABLED", True)
 
    app.config.setdefault("ZCASH_ENABLED", True)
 

	
 
    app.config.setdefault(
 
        "BITCOIN_CONVERSION_SCRIPT", "/usr/local/share/btc/BTC_Membership.pl"
 
    )
 
    app.config.setdefault(
 
        "ETHERUM_CONVERSION_SCRIPT", "/usr/local/share/eth/ETH_Membership.pl"
 
    )
 
    app.config.setdefault(
 
        "MONERO_CONVERSION_SCRIPT", "/usr/local/share/xmr/XMR_Membership.pl"
 
    )
 
    app.config.setdefault(
 
        "ZCASH_CONVERSION_SCRIPT", "/usr/local/share/zcash/ZCASH_Membership.pl"
 
    )
 

	
 
    from .views import root_page
 

	
 
    app.register_blueprint(root_page)
 

	
 
    return app
 

	
 

	
 
app = create_app()
c3l_membership/forms.py
Show inline comments
 
from datetime import date
 

	
 
from flask_wtf import Form
 
from wtforms import BooleanField, RadioField, StringField, SubmitField
 
from wtforms.fields.html5 import DateField
 
from wtforms.validators import Email, InputRequired, Length, Optional, Required
 
from flask_wtf import FlaskForm
 
from wtforms import BooleanField, DateField, RadioField, StringField, SubmitField
 
from wtforms.validators import Email, InputRequired, Length, Optional
 

	
 

	
 
class MembershipForm(Form):
 
class MembershipForm(FlaskForm):
 

	
 
    username = StringField(
 
        "Username",
 
        validators=[InputRequired("This field is required!"), Length(max=255)],
 
    )
 
    email = StringField(
 
        "E-Mail", validators=[InputRequired("This field is required!"), Email()]
 
    )
 
    fullname = StringField(
 
        "Full Name",
 
        validators=[InputRequired("This field is required!"), Length(max=65536)],
 
    )
 

	
 
    membership = RadioField(
 
        "Membership Plan",
 
        validators=[InputRequired("Please select one of the options!")],
 
        choices=[
 
            (
 
                "regular",
 
                "Regular membership - Membership with voting rights on the general assembly.",
 
            ),
 
            (
 
                "supporting",
 
                "Supporting membership - Membership without voting rights on the general assembly.",
 
            ),
 
        ],
 
    )
 

	
 
    student = BooleanField(
 
        ("I am a student and would like to have the reduced membership fees."),
 
    )
 

	
 
    starving = BooleanField(
 
        (
 
            "I am a starving hacker and cannot afford the membership! (Please get in touch with us at info@c3l.lu before filling out this membership form)"
 
        )
 
    )
 

	
 
    payment = RadioField(
 
        "Payment Options",
 
        validators=[InputRequired("Please select one of the options!")],
 
        choices=[
 
            ("cash", "by cash"),
 
            ("wire transfer", "by wire transfer"),
 
            ("digicash", "by DigiCash"),
 
            ("bitcoin", "by bitcoin"),
 
            ("ethereum", "by ethereum"),
 
        ],
 
    )
 

	
 
    birthday = DateField("Birthday")
 

	
 
    street = StringField(
 
        "Nr., Street",
 
        validators=[Length(max=4000)],
 
    )
 
    zip = StringField(
 
        "Postal Code",
 
        validators=[Length(max=30)],
 
    )
 

	
 
    city = StringField(
 
        "City/Town",
 
        validators=[Length(max=500)],
 
    )
 

	
 
    state = StringField(
 
        "State/County/Province",
 
        validators=[Length(max=500)],
 
    )
 

	
 
    country = StringField(
 
        "Country",
 
        validators=[Length(max=500)],
 
    )
 

	
 
    terms = BooleanField(
 
        (
 
            "By submitting this membership application, you agree to have read and understood "
 
            'the <a href="http://statutes.c3l.lu">statutes of the Chaos Computer Club Lëtzebuerg A.S.B.L.</a>'
 
        ),
 
        validators=[InputRequired()],
 
    )
 

	
 
    minor_member = BooleanField(
 
        (
 
            "I am under 18 years of age and have the approval of my legal representative."
 
        ),
 
    )
 

	
 
    submit = SubmitField("Become a member")
c3l_membership/templates/index.html
Show inline comments
 
@@ -66,49 +66,49 @@
 
                {{ form.student }} {{ form.student.label.text|safe}}
 
                </label>
 
              </div>
 
            </div>
 
            <div class="pure-g">
 
              <div class="pure-u-1-1">
 
                <label for="{{ form.starving.id }}" class="pure-checkbox">
 
                  {{ form.starving }} {{ form.starving.label.text|safe}}
 
                </label>
 
              </div>
 
            </div>
 
            <h3>What's difference between the different membership options?</h3>
 
            <p>All the options include to following benefits:</p>
 
            <ul>
 
              <li>Access to the <a href="https://wiki.c3l.lu/doku.php?id=organization:membership#benefits">services</a> run by us</li>
 
              <li>Access to our <a href="https://wiki.c3l.lu/doku.php?id=chaosstuff:bootstrap">hackerspace: ChaosStuff</a></li>
 
            </ul>
 
            <p>Becoming a regular member gives you these additional benefits:</p>
 
            <ul>
 
              <li>Voting rights on general assemblys</li>
 
              <li>Access to our internal mailinglist</li>
 
            </ul>
 
            <p>You can read more on the membership <a href="https://wiki.c3l.lu/doku.php?id=organization:membership">here</a>.</p>
 
            <h3>What are the membership fees?</h3>
 
            <p>The membership fee for the regular membership is 120€ per year. The membership fee for the supporting membership is 40€ per year. If you are a student, all membership fees are 40€ per year. For that please select the corresponding option.</p>
 
            <p>The membership fee for the regular membership is {{ config["REGULAR_FEE"] }}€ per year. The membership fee for the supporting membership is {{ config["SUPPORTING_FEE"] }}€ per year. If you are a student, all membership fees are {{ config["SUPPORTING_FEE"] }}€ per year. For that please select the corresponding option.</p>
 
            <h3>I cannot afford the membership?</h3>
 
            <p>If you cannot afford the membership, please contact us via info@c3l.lu first, before filling out the membership form. We will try to find a solution together.</p>
 
          </div>
 
        </div>
 
        <div class="pure-g">
 
          <div class="pure-u-1-1">
 
            <b>{{ form.payment.label }}</b>
 
            {% for option in form.payment %}
 
            <label for="{{ option.id }}" class="pure-radio pure-u-1-1">
 
              {{ option() }}
 
              {{ option.label.text }}
 
            </label>
 
            {% endfor %}
 
          </div>
 
        </div>
 
        <legend>Additional information</legend>
 
        <div class="pure-g">
 
          <div class="pure-u-1-1">
 
            <b>{{ form.birthday.label }}</b>
 
            {{ form.birthday(class="pure-u-1-1") }}
 
          </div>
 
        </div>
 
        <div class="pure-g">
 
          <div class="pure-u-1-1">
c3l_membership/templates/member.html
Show inline comments
 
@@ -70,70 +70,95 @@
 
      <div>
 
        <div>Agreed to Terms &amp; Conditions:</div>
 
        <div>{{ "Yes" if form.terms.data else "No" }}</div>
 
      </div>
 
      <div>
 
        <div>Minor Member:</div>
 
        <div>{{ "Yes" if form.minor_member.data else "No" }}</div>
 
      </div>
 
    </div>
 
    <p>Send this document to the Chaos Computer Club Lëtzebuerg!</p>
 
    {% if form.payment.data == 'wire transfer' %}
 
      <ul class="bank">
 
        <li>Account Holder: Chaos Computer Club Lëtzebuerg</li>
 
        <li>BIC/Swift: BCEELULLXXX</li>
 
        <li>IBAN: LU29 0019 2855 3890 4000</li>
 
        <li>Message: Membership fee {{ year }} {{ form.username.data }}</li>
 
        <li>Amount: {{ price }}€</li>
 
      </ul>
 
    {% elif form.payment.data == 'cash' %}
 
      <p>Please bring {{ price }}€ with you the next time you meet us!</p>
 
    {% elif form.payment.data == 'bitcoin' %}
 
      <div class="btccontainer">
 
        <div>
 
          <ul class="btctext">
 
            <li><b>Address:</b> 3BV5UPToMUmXJiqUVndHjSSq2HqDW8npRd</li>
 
            <li><b>Address:</b> {{ config["BITCOIN_ADDRESS"] }}</li>
 
            <li><b>Label:</b> Membership Fee</li>
 
            <li><b>Message:</b> {{ year }} {{ form.username.data }}</li>
 
            <li><b>Amount:</b> {{ price }} BTC</li>
 
          </ul>
 
        </div>
 
        {% set bitcoin_url="bitcoin:3BV5UPToMUmXJiqUVndHjSSq2HqDW8npRd?amount={amount}&label=Membership Fee {year} {username}&message=Membership Fee {year} {username}".format(amount=price, year=year, username=form.username.data) %}
 
        {% set bitcoin_url="bitcoin:" + config["BITCOIN_ADDRESS"] + "?amount={amount}&label=Membership Fee {year} {username}&message=Membership Fee {year} {username}".format(amount=price, year=year, username=form.username.data) %}
 
        <div><img class="btcqrcode" src="{{ qrcode(bitcoin_url) }}"></img></div>
 
      </div>
 
    {% elif form.payment.data == 'ethereum' %}
 
      <div class="btccontainer">
 
        <div>
 
          <ul class="btctext">
 
            <li><b>Address:</b> 0x1C05b4C236914B218B57e0E25948d416B4885655</li>
 
            <li><b>Address:</b> {{ config["ETHEREUM_ADDRESS"] }}</li>
 
            <li><b>Label:</b> Membership Fee</li>
 
            <li><b>Message:</b> {{ year }} {{ form.username.data }}</li>
 
            <li><b>Amount:</b> {{ price }} ETH</li>
 
          </ul>
 
        </div>
 
        {% set ethereum_url="0x1C05b4C236914B218B57e0E25948d416B4885655" %}
 
        {% set ethereum_url=config["ETHEREUM_ADDRESS"] %}
 
        <div><img class="btcqrcode" src="{{ qrcode(ethereum_url) }}"></img></div>
 
      </div>
 
    {% elif form.payment.data == 'monero' %}
 
      <div class="btccontainer">
 
        <div>
 
          <ul class="btctext">
 
            <li><b>Address:</b> {{ config["MONERO_ADDRESS"] }}</li>
 
            <li><b>Label:</b> Membership Fee</li>
 
            <li><b>Message:</b> {{ year }} {{ form.username.data }}</li>
 
            <li><b>Amount:</b> {{ price }} XMR</li>
 
          </ul>
 
        </div>
 
        {% set monero_url=config["MONERO_ADDRESS"] %}
 
        <div><img class="btcqrcode" src="{{ qrcode(monero_url) }}"></img></div>
 
      </div>
 
    {% elif form.payment.data == 'zcash' %}
 
      <div class="btccontainer">
 
        <div>
 
          <ul class="btctext">
 
            <li><b>Address:</b> {{ config["ZCASH_ADDRESS"] }}</li>
 
            <li><b>Label:</b> Membership Fee</li>
 
            <li><b>Message:</b> {{ year }} {{ form.username.data }}</li>
 
            <li><b>Amount:</b> {{ price }} ZEC</li>
 
          </ul>
 
        </div>
 
        {% set zcash_url=config["ZCASH_ADDRESS"] %}
 
        <div><img class="btcqrcode" src="{{ qrcode(zcash_url) }}"></img></div>
 
      </div>
 
    {% elif form.payment.data == 'digicash' %}
 
      <div class="digicash">
 
        {% set digicash_url='https://pos.digica.sh/qrcode/generator?merchantId=CHAOSPC1&amount={amount}&transactionReference=Membership_{username}'.format(amount=price, username=form.username.data) %}
 
        <div><p>Pay with digicash!</p></div>
 
        <div><img src="{{ digicash_url }}"></img></div>
 
      </div>
 
    {% endif %}
 
    <div class="signature">
 
      <p class="membersignature">
 
        Luxembourg, the 
 
      </p>
 
      <p class="adminsignature">
 
        {{ "Signature of your legal representative" if form.minor_member.data else "Your signature" }}
 
      </p>
 
    </div>
 
    <footer>
 
      <hr>
 
      <b>C</b>haos <b>C</b>omputer <b>C</b>lub <b>L</b>ëtzebuerg A.S.B.L.<br>
 
      Halle Victor Hugo - 60 Avenue Victor Hugo L-1750 Luxembourg (Europe)<br>
 
      info@c3l.lu - <a href="https://c3l.lu">http://c3l.lu</a>
 
      <!--<i>Registered as an NPO with the “Registre de Commerce et des Sociétés Luxembourg” with the registration number: <a href="http://www.legilux.public.lu/entr/search/index.php?ss_soc=chaos&sc_soc=&sr_soc=name&sr_fj=all&sr_tp=all&sr_date=all&sl_d=1&sl_m=1&sl_y=2014&sl_d1=1&sl_m1=1&sl_y1=2014&sl_d2=1&sl_m2=2&sl_y2=2014&page_len=100&page_no=1&idm=20086101433&page=result&select_soc=true&submit=Choisir">F0007653</a></i>-->
 
    </footer>
 
  </body>
c3l_membership/views.py
Show inline comments
 
import re
 
import subprocess
 
from datetime import date
 

	
 
from flask import Blueprint, current_app, render_template, request
 
from flask_weasyprint import HTML, render_pdf
 

	
 
from .forms import MembershipForm
 

	
 
root_page = Blueprint("root", __name__)
 

	
 
xml_template = "<member><numm>{name}</numm><gebuertsdag>{birthday:%d.%m.%Y}</gebuertsdag><address>{address}</address><nick>{username}</nick><email>{email}</email><status>{status}</status><stemmrecht>{voting}</stemmrecht></member>"
 

	
 

	
 
@root_page.route("/", methods=("GET", "POST"))
 
def index():
 
    form = MembershipForm()
 

	
 
    choices = [("cash", "by cash"), ("wire transfer", "by wire transfer")]
 

	
 
    for k, v in (
 
        ("DIGICASH_ENABLED", ("digicash", "by DigiCash")),
 
        ("BITCOIN_ENABLED", ("bitcoin", "by bitcoin")),
 
        ("ETHEREUM_ENABLED", ("ethereum", "by ethereum")),
 
        ("MONERO_ENABLED", ("monero", "by monero")),
 
        ("ZCASH_ENABLED", ("zcash", "by zcash")),
 
    ):
 
        if current_app.config[k]:
 
            choices.append(v)
 
    form.payment.choices = choices
 

	
 
    if form.validate_on_submit():
 

	
 
        xml_data = {}
 

	
 
        if (
 
            form.minor_member.data
 
            or form.student.data
 
            or form.membership.data == "supporting"
 
        ):
 
            price = 40
 
            price = current_app.config["SUPPORTING_FEE"]
 
            xml_data["voting"] = 0
 
        elif form.membership.data == "regular":
 
            price = 120
 
            price = current_app.config["REGULAR_FEE"]
 
            xml_data["voting"] = 1
 

	
 
        if form.starving.data:
 
            price = 1
 

	
 
        if form.starving.data:
 
            xml_data["status"] = "Starving"
 
        elif form.minor_member.data or form.student.data:
 
            xml_data["status"] = "Student"
 
        elif form.membership.data == "supporting":
 
            xml_data["status"] = "Supporter"
 
        else:
 
            xml_data["status"] = "Regular"
 

	
 
        xml_data["name"] = form.fullname.data
 
        xml_data["birthday"] = form.birthday.data
 
        xml_data["username"] = form.username.data
 
        xml_data["email"] = form.email.data
 
        xml_data["address"] = " ".join(
 
            (
 
                form.street.data,
 
                form.zip.data,
 
                form.city.data,
 
                form.state.data,
 
                form.country.data,
 
            )
 
        )
 
        xml_data["address"] = re.sub("\s+", " ", xml_data["address"])
 

	
 
        if form.payment.data == "bitcoin":
 
            price = subprocess.check_output(
 
                ["/usr/local/share/btc/BTC_Membership.pl", str(price)]
 
                [current_app.config["BITCOIN_CONVERSION_SCRIPT"], str(price)]
 
            ).decode("utf-8")
 
        elif form.payment.data == "ethereum":
 
            price = subprocess.check_output(
 
                ["/usr/local/share/eth/ETH_Membership.pl", str(price)]
 
                [current_app.config["ETHERUM_CONVERSION_SCRIPT"], str(price)]
 
            ).decode("utf-8")
 
        elif form.payment.data == "monero":
 
            price = subprocess.check_output(
 
                ["/usr/local/share/xmr/XMR_Membership.pl", str(price)]
 
                [current_app.config["MONERO_CONVERSION_SCRIPT"], str(price)]
 
            ).decode("utf-8")
 
        elif form.payment.data == "zcash":
 
            price = subprocess.check_output(
 
                [current_app.config["ZCASH_CONVERSION_SCRIPT"], str(price)]
 
            ).decode("utf-8")
 
        elif form.payment.data == "digicash":
 
            price = price * 100
 

	
 
        now = date.today()
 
        year = now.year
 
        xml_data["date"] = now
 

	
 
        xml = xml_template.format(**xml_data)
 
        html = render_template(
 
            "member.html", form=form, price=price, year=year, xml=xml
 
        )
 
        return render_pdf(HTML(string=html))
 
    return render_template("index.html", form=form)
0 comments (0 inline, 0 general)