Skip to content

Commit

Permalink
Show 'Phone home request' if not shown before
Browse files Browse the repository at this point in the history
  • Loading branch information
stijn-uva committed Jan 25, 2023
1 parent f632b0c commit 0da1fb1
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 83 deletions.
1 change: 1 addition & 0 deletions backend/workers/restart_4cat.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,5 @@ def work(self):

log_stream_restart.close()
lock_file.unlink()

self.job.finish()
7 changes: 7 additions & 0 deletions common/lib/config_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@
"is sent to this URL to help the 4CAT developers (the Digital Methods Initiative) keep track of how "
"much it is used. There should be no need to change this URL after installation."
},
"4cat.phone_home_asked": {
"type": UserInput.OPTION_TOGGLE,
"default": False,
"help": "Shown phone home request?",
"tooltip": "Whether you've seen the 'phone home request'. Set to `false` to see the request again. There "
"should be no need to change this manually."
},
# These settings control whether top-level datasets (i.e. those created via the
# "Create dataset" page) are deleted automatically, and if so, after how much
# time. You can also allow users to cancel this (i.e. opt out). Note that if
Expand Down
1 change: 1 addition & 0 deletions helper-scripts/migrate/migrate-1.29-1.30.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
config.delete_setting("DATASOURCES")

print(" Deleting and migrating deprecated settings...")
config.set_or_create_setting("4cat.phone_home_asked", False, raw=False)
if config.get("IMAGE_INTERVAL"):
print(" - IMAGE_INTERVAL -> fourchan.image_interval")
config.set_or_create_setting("fourchan.image_interval", config.get("IMAGE_INTERVAL", 60), raw=False)
Expand Down
27 changes: 27 additions & 0 deletions webtool/templates/account/first-run-after-update.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends "layout.html" %}

{% block title %}Complete 4CAT Setup{% endblock %}
{% block body_class %}plain-page markdown-page {{ body_class }}{% endblock %}

{% block body %}
<article class="small">
<section>
<h2><span>4CAT has been upgraded</span></h2>
<p>4CAT has been upgraded! The current version is <em>{{ version }}</em>. You can now resume using it.</p>

{% if phone_home_url %}
<form action="{{ url_for('first_run_dialog') }}" method="POST" class="wide">
{% for notice in flashes %}
<p class="form-notice">{{ notice }}</p>
{% endfor %}

{% include "account/pingback-form.html" %}

<div class="submit-container">
<button>Submit</button>
</div>
</form>
{% endif %}
</section>
</article>
{% endblock %}
49 changes: 2 additions & 47 deletions webtool/templates/account/first-run.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ <h2><span>Welcome to 4CAT!</span></h2>
<p>This will only set up your initial account. To add more accounts, after logging in, use the Control Panel or
configure e-mail server details to enable users to request their own accounts.</p>

<form action="{{ url_for('create_first_user') }}" method="POST" class="wide">
<form action="{{ url_for('first_run_dialog') }}" method="POST" class="wide">
{% for notice in flashes %}
<p class="form-notice">{{ notice }}</p>
{% endfor %}
Expand All @@ -32,52 +32,7 @@ <h2><span>Welcome to 4CAT!</span></h2>
</fieldset>

{% if phone_home_url %}
<fieldset>
<div class="form-element full-width form-intro">
<label for="request-phonehome">
<input type="checkbox" id="request-phonehome" class="toggle-button" name="phonehome"{% if form.phonehome %} checked="checked"{% endif %} aria-controls="phone-home-fields">
Let the 4CAT developers know you've installed it
</label>
</div>

<p>Letting us (the 4CAT developers at the <a href="https://digitalmethods.net">Digital
Methods Initiative</a>) know that you have installed 4CAT is <em>optional but greatly appreciated</em>
as it helps us justify and find funding for on-going development.</p>
<p>We only store the data you enter, and the approximate geographic location of the IP address it is sent
from (but not the IP address itself). This is a <em>one-time</em> 'ping': no information will be sent
during later 4CAT usage. You can check our website for <a href="{{ phone_home_url }}">more
information</a> on what we do with this data.</p>

<div id="phone-home-fields" aria-expanded="false">
<p>The following fields are optional. If you enter contact information, we may contact you to learn more
about how you are using 4CAT, but we will <em>never</em> send any automated newsletters or mailings.</p>
<div class="form-element full-width">
<label for="request-role">What best describes your role?</label>
<select name="role" id="request-role">
<option value=""></option>
<option value="student">Student</option>
<option value="phd">PhD candidate</option>
<option value="researcher">Academic researcher</option>
<option value="support-staff">Other staff at an academic institute (e.g. IT support)</option>
<option value="researcher">Independent researcher</option>
<option value="journalist">Journalist</option>
<option value="ngo">I work at an NGO</option>
<option value="commercial">I work for a commercial business</option>
<option value="other">Other</option>
</select>
</div>

<div class="form-element full-width">
<label for="request-affiliation">What is the name of the institute, organisation or company you are installing 4CAT for (if applicable)?</label>
<input name="affiliation" value="" id="request-affiliation" type="text" placeholder="E.g. University or company name">
</div>

<div class="form-element full-width">
<label for="request-email">What e-mail address can we contact you at?</label>
<input name="email" value="" id="request-email" type="text">
</div>
</div>
</fieldset>
{% include "account/pingback-form.html" %}
{% endif %}

<div class="submit-container">
Expand Down
46 changes: 46 additions & 0 deletions webtool/templates/account/pingback-form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<fieldset>
<div class="form-element full-width form-intro">
<label for="request-phonehome">
<input type="checkbox" id="request-phonehome" class="toggle-button" name="phonehome"{% if form.phonehome %} checked="checked"{% endif %} aria-controls="phone-home-fields">
Let the 4CAT developers know you've installed it
</label>
</div>

<p>Letting us (the 4CAT developers at the <a href="https://digitalmethods.net">Digital
Methods Initiative</a>) know that you have installed 4CAT is <em>optional but greatly appreciated</em>
as it helps us justify and find funding for on-going development.</p>
<p>We only store the data you enter, and the approximate geographic location of the IP address it is sent
from (but not the IP address itself). This is a <em>one-time</em> 'ping': no information will be sent
during later 4CAT usage. You can check our website for <a href="{{ phone_home_url }}">more
information</a> on what we do with this data.</p>

<div id="phone-home-fields" aria-expanded="false">
<p>The following fields are optional. If you enter contact information, we may contact you to learn more
about how you are using 4CAT, but we will <em>never</em> send any automated newsletters or mailings.</p>
<div class="form-element full-width">
<label for="request-role">What best describes your role?</label>
<select name="role" id="request-role">
<option value=""></option>
<option value="student">Student</option>
<option value="phd">PhD candidate</option>
<option value="researcher">Academic researcher</option>
<option value="support-staff">Other staff at an academic institute (e.g. IT support)</option>
<option value="researcher">Independent researcher</option>
<option value="journalist">Journalist</option>
<option value="ngo">I work at an NGO</option>
<option value="commercial">I work for a commercial business</option>
<option value="other">Other</option>
</select>
</div>

<div class="form-element full-width">
<label for="request-affiliation">What is the name of the institute, organisation or company you are installing 4CAT for (if applicable)?</label>
<input name="affiliation" value="" id="request-affiliation" type="text" placeholder="E.g. University or company name">
</div>

<div class="form-element full-width">
<label for="request-email">What e-mail address can we contact you at?</label>
<input name="email" value="" id="request-email" type="text">
</div>
</div>
</fieldset>
1 change: 0 additions & 1 deletion webtool/views/views_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ def show_frontpage():

datasources = {k: v for k, v in backend.all_modules.datasources.items() if k in config.get("4cat.datasources") and not v["importable"]}
importables = {k: v for k, v in backend.all_modules.datasources.items() if v["importable"]}
print(importables)

return render_template("frontpage.html", stats=stats, news=news, datasources=datasources, importables=importables)

Expand Down
95 changes: 60 additions & 35 deletions webtool/views/views_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ def load_user_from_request(request):


@app.before_request
def banned_users():
def reroute_requests():
"""
Displays a 'sorry, no 4cat for you' message to banned or deactivated users.
Sometimes the requested route should be overruled
"""

# Displays a 'sorry, no 4cat for you' message to banned or deactivated users.
if current_user and current_user.is_authenticated and current_user.is_deactivated:
message = "Your 4CAT account has been deactivated and you can no longer access this page."
if current_user.get_attribute("deactivated.reason"):
Expand All @@ -84,6 +86,13 @@ def banned_users():

return render_template("error.html", title="Your account has been deactivated", message=message), 403

# ensures admins get to see the phone home screen at least once
elif current_user and current_user.is_authenticated and current_user.is_admin and \
request.url_rule and request.url_rule.endpoint not in ("static", "first_run_dialog"):
wants_phone_home = not config.get("4cat.phone_home_asked", False)
if wants_phone_home:
return redirect(url_for("first_run_dialog"))


@app.before_request
def autologin_whitelist():
Expand Down Expand Up @@ -154,7 +163,7 @@ def exempt_from_limit():


@app.route("/first-run/", methods=["GET", "POST"])
def create_first_user():
def first_run_dialog():
"""
Special route for creating an initial admin user
Expand All @@ -163,38 +172,59 @@ def create_first_user():
:return:
"""
has_admin_user = db.fetchone("SELECT COUNT(*) AS amount FROM users WHERE is_admin = True")
has_admin_user = db.fetchone("SELECT COUNT(*) AS amount FROM users WHERE is_admin = True")["amount"]
wants_phone_home = not config.get("4cat.phone_home_asked", False)

version_file = Path(config.get("PATH_ROOT"), "config/.current-version")
if version_file.exists():
with version_file.open() as infile:
version = infile.readline().strip()
else:
version = "unknown"

missing = []
if has_admin_user["amount"]:
if has_admin_user and not wants_phone_home:
return error(403, message="The 'first run' page is not available")

phone_home_url = config.get("4cat.phone_home_url")
if request.method == 'GET':
return render_template("account/first-run.html", incomplete=missing, form=request.form, phone_home_url=phone_home_url)
template = "account/first-run.html" if not has_admin_user else "account/first-run-after-update.html"
return render_template(template, incomplete=missing, form=request.form, phone_home_url=phone_home_url, version=version)

username = request.form.get("username").strip()
password = request.form.get("password").strip()
confirm_password = request.form.get("confirm_password").strip()
if not has_admin_user:
username = request.form.get("username").strip()
password = request.form.get("password").strip()
confirm_password = request.form.get("confirm_password").strip()

if not username:
missing.append("username")
else:
user_exists = db.fetchone("SELECT name FROM users WHERE name = %s", (username,))
if user_exists:
flash("The username '%s' already exists and is reserved." % username)
if not username:
missing.append("username")
else:
user_exists = db.fetchone("SELECT name FROM users WHERE name = %s", (username,))
if user_exists:
flash("The username '%s' already exists and is reserved." % username)
missing.append("username")

if not password:
missing.append("password")
elif password != confirm_password:
flash("The passwords provided do not match")
missing.append("password")
missing.append("confirm_password")

if missing:
flash("Please make sure all fields are complete")
return render_template("account/first-run.html", form=request.form, incomplete=missing,
flashes=get_flashed_messages(), phone_home_url=phone_home_url)

if not password:
missing.append("password")
elif password != confirm_password:
flash("The passwords provided do not match")
missing.append("password")
missing.append("confirm_password")
db.insert("users", data={"name": username})
db.commit()
user = User.get_by_name(db=db, name=username)
user.set_password(password)

if missing:
flash("Please make sure all fields are complete")
return render_template("account/first-run.html", form=request.form, incomplete=missing,
flashes=get_flashed_messages(), phone_home_url=phone_home_url)
db.update("users", where={"name": username}, data={"is_admin": True, "is_deactivated": False})
db.commit()

flash("The admin user '%s' was created, you can now use it to log in." % username)

if phone_home_url and request.form.get("phonehome"):
with Path(config.get("PATH_ROOT"), "config/.current-version").open() as outfile:
Expand All @@ -214,16 +244,11 @@ def create_first_user():
# too bad
flash("Could not send install ping to 4CAT developers")

db.insert("users", data={"name": username})
db.commit()
user = User.get_by_name(db=db, name=username)
user.set_password(password)

db.update("users", where={"name": username}, data={"is_admin": True, "is_deactivated": False})
db.commit()
# don't ask phone home again until next update
config.set_or_create_setting("4cat.phone_home_asked", True, raw=False)

flash("The admin user '%s' was created, you can now use it to log in." % username)
return redirect(url_for("show_login"))
redirect_path = "show_login" if not has_admin_user else "show_frontpage"
return redirect(url_for(redirect_path))


@app.route('/login/', methods=['GET', 'POST'])
Expand All @@ -241,7 +266,7 @@ def show_login():

has_admin_user = db.fetchone("SELECT * FROM users WHERE is_admin = True")
if not has_admin_user:
return redirect(url_for("create_first_user"))
return redirect(url_for("first_run_dialog"))

have_email = config.get('mail.admin_email') and config.get('mail.server')
if request.method == 'GET':
Expand Down

0 comments on commit 0da1fb1

Please sign in to comment.