Skip to content

Commit

Permalink
Merge branch 'main' into weblate-hedy-adventures
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Jun 5, 2024
2 parents e88ffb8 + 37e0a59 commit 3bdbe59
Show file tree
Hide file tree
Showing 76 changed files with 880 additions and 17 deletions.
10 changes: 6 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@
from website import (ab_proxying, achievements, admin, auth_pages, aws_helpers,
cdn, classes, database, for_teachers, s3_logger, parsons,
profile, programs, querylog, quiz, statistics,
translating, tags, surveys, public_adventures, user_activity, feedback)
from website.auth import (current_user, hide_explore, is_admin, is_teacher, is_second_teacher, has_public_profile,
translating, tags, surveys, super_teacher, public_adventures, user_activity, feedback)
from website.auth import (current_user, is_admin, is_teacher, is_second_teacher, is_super_teacher, has_public_profile,
login_user_from_token_cookie, requires_login, requires_login_redirect, requires_teacher,
forget_current_user)
forget_current_user, hide_explore)
from website.log_fetcher import log_fetcher
from website.frontend_types import Adventure, Program, ExtraStory, SaveInfo

Expand Down Expand Up @@ -326,7 +326,7 @@ def initialize_session():

g.user = current_user()
querylog.log_value(session_id=utils.session_id(), username=g.user['username'],
is_teacher=is_teacher(g.user), is_admin=is_admin(g.user))
is_teacher=is_teacher(g.user), is_admin=is_admin(g.user), is_super_teacher=is_admin(g.user))


if os.getenv('IS_PRODUCTION'):
Expand Down Expand Up @@ -439,6 +439,7 @@ def enrich_context_with_user_info():
data = {'username': user.get('username', ''),
'is_teacher': is_teacher(user), 'is_second_teacher': is_second_teacher(user),
'is_admin': is_admin(user), 'has_public_profile': has_public_profile(user),
'is_super_teacher': is_super_teacher(user),
'hide_explore': hide_explore(g.user)}
return data

Expand Down Expand Up @@ -2775,6 +2776,7 @@ def current_user_allowed_to_see_program(program):
app.register_blueprint(for_teachers.ForTeachersModule(DATABASE, ACHIEVEMENTS))
app.register_blueprint(classes.ClassModule(DATABASE, ACHIEVEMENTS))
app.register_blueprint(classes.MiscClassPages(DATABASE, ACHIEVEMENTS))
app.register_blueprint(super_teacher.SuperTeacherModule(DATABASE))
app.register_blueprint(admin.AdminModule(DATABASE))
app.register_blueprint(achievements.AchievementsModule(ACHIEVEMENTS))
app.register_blueprint(quiz.QuizModule(DATABASE, ACHIEVEMENTS, QUIZZES))
Expand Down
6 changes: 6 additions & 0 deletions messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ msgstr ""
msgid "adventure_exp_classes"
msgstr ""

msgid "adventure_flagged"
msgstr ""

msgid "adventure_id_invalid"
msgstr ""

Expand Down Expand Up @@ -674,6 +677,9 @@ msgstr ""
msgid "female"
msgstr ""

msgid "flag_adventure_prompt"
msgstr ""

msgid "float"
msgstr ""

Expand Down
42 changes: 40 additions & 2 deletions static/js/appbundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -60460,13 +60460,15 @@ ${o3}` : i3;
initializeTutorial: () => initializeTutorial,
initializeViewProgramPage: () => initializeViewProgramPage,
invite_student: () => invite_student,
invite_support_teacher: () => invite_support_teacher,
join_class: () => join_class,
loadParsonsExercise: () => loadParsonsExercise,
load_quiz: () => load_quiz,
load_variables: () => load_variables,
logout: () => logout,
logs: () => logs,
markAsTeacher: () => markAsTeacher,
markSuperTeacher: () => markSuperTeacher,
markUnsavedChanges: () => markUnsavedChanges,
modal: () => modal,
modalStepOne: () => modalStepOne,
Expand Down Expand Up @@ -104179,6 +104181,24 @@ def note_with_error(value, err):
}
});
}
function invite_support_teacher(requester) {
modal.prompt(`Invite a teacher to support ${requester}.`, "", function(username) {
$.ajax({
type: "POST",
url: "/super-teacher/invite-support",
data: JSON.stringify({
sourceUser: requester,
targetUser: username
}),
contentType: "application/json",
dataType: "json"
}).done(function() {
location.reload();
}).fail(function(err) {
modal.notifyError(err.responseText);
});
});
}

// static/js/comm.ts
function postJson(url, data) {
Expand Down Expand Up @@ -104401,15 +104421,15 @@ def note_with_error(value, err):
}
});
}
function markAsTeacher(checkbox, username, is_teacher, pending_request) {
function markAsTeacher(checkbox, username, is_teacher, pending_request, by_super_teacher = false) {
$(checkbox).prop("checked", false);
let text = "Are you sure you want to remove " + username + " as a teacher?";
if (is_teacher) {
text = "Are you sure you want to make " + username + " a teacher?";
}
modal.confirm(text, async () => {
try {
await postNoResponse("/admin/markAsTeacher", {
await postNoResponse(by_super_teacher ? "/super-teacher/markAsTeacher" : "/admin/markAsTeacher", {
username,
is_teacher
});
Expand All @@ -104428,6 +104448,24 @@ def note_with_error(value, err):
}
});
}
function markSuperTeacher(checkbox, username, is_super_teacher) {
let text = "Are you sure you want to make " + username + " a super teacher?";
if (is_super_teacher) {
text = "Are you sure you want to revoke super teacher privilege from " + username + "?";
}
modal.confirm(text, async () => {
try {
await postNoResponse("/admin/mark-super-teacher", {
username
});
location.reload();
} catch (e) {
console.error(e);
modal.notifyError(e);
$(checkbox).prop("checked", is_super_teacher);
}
}, () => $(checkbox).prop("checked", is_super_teacher));
}
function changeUserEmail(username, email) {
modal.prompt("Please enter the corrected email", email, async function(correctedEmail) {
if (correctedEmail === email)
Expand Down
4 changes: 2 additions & 2 deletions static/js/appbundle.js.map

Large diffs are not rendered by default.

25 changes: 23 additions & 2 deletions static/js/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,15 @@ export function initializeFormSubmits() {

// *** Admin functionality ***

export function markAsTeacher(checkbox: any, username: string, is_teacher: boolean, pending_request: boolean) {
export function markAsTeacher(checkbox: any, username: string, is_teacher: boolean, pending_request: boolean, by_super_teacher = false) {
$(checkbox).prop('checked', false);
let text = "Are you sure you want to remove " + username + " as a teacher?";
if (is_teacher) {
text = "Are you sure you want to make " + username + " a teacher?";
}
modal.confirm (text, async () => {
try {
await postNoResponse('/admin/markAsTeacher', {
await postNoResponse(by_super_teacher ? '/super-teacher/markAsTeacher' : '/admin/markAsTeacher', {
username: username,
is_teacher: is_teacher,
});
Expand All @@ -213,6 +213,27 @@ export function markAsTeacher(checkbox: any, username: string, is_teacher: boole
});
}

export function markSuperTeacher(checkbox: any, username: string, is_super_teacher: boolean) {
let text = "Are you sure you want to make " + username + " a super teacher?";
if (is_super_teacher) {
text = "Are you sure you want to revoke super teacher privilege from " + username + "?";
}
modal.confirm (text, async () => {
try {
await postNoResponse('/admin/mark-super-teacher', {
username: username,
});
location.reload();
} catch (e: any) {
console.error(e);
modal.notifyError(e);
$(checkbox).prop('checked', is_super_teacher);
}
}, () => $(checkbox).prop('checked', is_super_teacher)
);
}


export function changeUserEmail(username: string, email: string) {
modal.prompt ('Please enter the corrected email', email, async function (correctedEmail) {
if (correctedEmail === email) return;
Expand Down
20 changes: 20 additions & 0 deletions static/js/teachers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,3 +750,23 @@ export function initializeClassOverviewPage(_options: InitializeClassOverviewPag
}
});
}


export function invite_support_teacher(requester: string) {
modal.prompt(`Invite a teacher to support ${requester}.`, '', function (username) {
$.ajax({
type: 'POST',
url: "/super-teacher/invite-support",
data: JSON.stringify({
sourceUser: requester,
targetUser: username,
}),
contentType: 'application/json',
dataType: 'json'
}).done(function() {
location.reload();
}).fail(function(err) {
modal.notifyError(err.responseText);
});
});
}
5 changes: 4 additions & 1 deletion templates/admin/admin-users.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ <h4 class="text-center">Total amount of shown users: {{ users|length }}</h4>
<td id="experience_header" class="hidden">Experience</td>
<td id="tags_header" class="text-center">Tags</td>
<td id="teacher_header" class="text-center">Teacher</td>
<td id="teacher_header" class="text-center">Super Teacher</td>
<td id="verified_header" class="hidden">Verified</td>
</tr>
</thead>
Expand All @@ -167,7 +168,9 @@ <h4 class="text-center">Total amount of shown users: {{ users|length }}</h4>
<td class="teacher_cell text-center"><span class="fa-solid fa-circle-question cursor-pointer" onclick="hedyApp.markAsTeacher (this, '{{user.username}}', true, true)"></span></td>
{% else %}
<td class="teacher_cell text-center"><input type="checkbox" {% if user.is_teacher %}checked="checked"{% endif %} onclick="hedyApp.markAsTeacher (this, '{{user.username}}', {% if user.is_teacher %}false{% else %}true{% endif %}, false)"></td>
{% endif %}<td class="verified_cell hidden">{{user.email_verified}}</td>
{% endif %}
<td class="teacher_cell text-center"><input type="checkbox" {% if user.is_super_teacher %}checked="checked"{% endif %} onclick="hedyApp.markSuperTeacher (this, '{{user.username}}', {% if user.is_super_teacher %}true{% else %}false{% endif %})"></td>
<td class="verified_cell hidden">{{user.email_verified}}</td>
<td class="third_party_cell hidden text-center"><input type="checkbox" {% if user.third_party %}checked="checked"{% endif %} ></td>
</tr>
{% endfor %}
Expand Down
64 changes: 64 additions & 0 deletions templates/feedback.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{% extends "auth.html" %}

{% block regular_content %}
<h1>Feedback Reports</h1>

<form>
<div class="flex flex-row px-4 w-full py-2 rounded-lg gap-4">
<select id="category-dropdown" name="category" class="block appearance-none w-1/8 bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 ltr:pr-8 rtl:pl-8 rounded" data-autosubmit="true">
<option selected value="">&mdash; Category &mdash;</option>
{% for category in categories %}
<option value="{{ category }}" {% if request.args.get('category') == category %}selected{% endif %}>{{ category }}</option>
{% endfor %}
</select>
<select id="page-dropdown" name="page" class="block appearance-none w-1/8 bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 ltr:pr-8 rtl:pl-8 rounded" data-autosubmit="true">
<option selected value="">&mdash; Page &mdash;</option>
{% for page in pages %}
<option value="{{ page }}" {% if request.args.get('page') == page %}selected{% endif %}>{{ page }}</option>
{% endfor %}
</select>
<select id="user-dropdown" name="username" class="block appearance-none w-1/8 bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 ltr:pr-8 rtl:pl-8 rounded" data-autosubmit="true">
<option selected value="">&mdash; User &mdash;</option>
{% for username in users %}
<option value="{{ username }}" {% if request.args.get('username') == username %}selected{% endif %}>{{ username }}</option>
{% endfor %}
</select>
</div>
</form>
</div>

{% for category, feedback_list in feedback_by_category.items() %}
<div class="flex flex-col gap-4 mb-8 md:px-16 " id="classes_table">
<div class="overflow-x-auto rounded-lg shadow-lg">
<h2>Category: <strong>{{category}}</strong></h2>
<table class="w-full border border-gray-400">
<thead>
<tr class="bg-blue-300 text-blue-900">
<th class="px-2 py-2 text-center">Username</th>
<th class="px-2 py-2 text-center">Email</th>
<th class="px-10 ">Message</th>
<th class="px-10 ">Page</th>
<th class="px-10 ">Category</th>
<th class="px-10 ">Date</th>
<th class="px-10 bg-blue-900 text-white">Github issue</th>
</tr>
</thead>
<tbody>
{% for feedback in feedback_list %}
<tr class="{% if loop.index is divisibleby 2 %}bg-gray-200{% else %}bg-white{% endif %}">
<td class="text-center px-4 py-2">{{ feedback.username }}</td>
<td id="teacher_cell" class="text-center p-2">{{ feedback.email }}</td>
<td class="text-center p-2">{{ feedback.message }}</td>
<td class="text-center p-2">{{ feedback.page }}</td>
<td class="text-center p-2">{{ feedback.category }}</td>
<td class="text-center p-2">{{ feedback.date|format_date }}</td>
<td class="text-center p-2"><a target="_blank"
href="https://github.com/hedyorg/hedy/issues/new?labels=feedback&title={{feedback.category}}&body=%23%20Description%0A{{feedback.message}}%0A%0A%23%20Page%0A{{feedback.page}}%0A%0A%23%20Date%0A{{feedback.date|format_date}}">Create issue</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
{% endblock %}
2 changes: 1 addition & 1 deletion templates/for-teachers.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div class = "bg-blue-500 border border-black rounded-lg px-8 py-4 text-center" >
<h2 id = "tutorial_title" class = "text-white" > </h2 >
<p id = "tutorial_text" > </p >
<button id = "tutorial_next_button" class = "green-btn mt-4" > {{_('next_step_tutorial')}} < /button >
<button id = "tutorial_next_button" class = "green-btn mt-4" > {{_('next_step_tutorial')}} </button>
</div >
</div>
{% endif %}
Expand Down
14 changes: 14 additions & 0 deletions templates/htmx-adventure-card.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@
class="green-btn" id="edit_adventure_button">
{{_('edit_adventure')}}</a>
{% endif %}
<span class="fa fa-flag bg-{% if adventure.flagged %}red{%else%}blue{%endif%}-300 hover:bg-blue-600 p-2 mx-4 rounded-full text-white cursor-pointer"
hx-confirm="{{_('flag_adventure_prompt')}}"
hx-trigger="click"
hx-post="/public-adventures/flag/{{adventure.id}}/{{adventure.flagged}}"
hx-swap="none"
_="on htmx:afterRequest if detail.xhr.status == 200 then window.location.reload()"></span>
{% if is_super_teacher %}
<i class="fa fa-trash p-2 bg-red-500 mr-4 rounded-full cursor-pointer" id="{{adventure.id}}"
hx-confirm="{{_('delete_adventure_prompt')}}"
hx-trigger="click"
hx-delete="/for-teachers/customize-adventure/{{adventure.id}}/{{adventure.creator}}"
hx-swap="none"
_="on htmx:afterRequest if detail.xhr.status == 200 then window.location.reload()"></i>
{% endif %}
{% endif %}
</div>
</div>
3 changes: 3 additions & 0 deletions templates/incl/menubar.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
{% if is_admin %}
<li class="flex-initial truncate menubar-item{% if current_page == 'admin' %} active{% endif %}"><a class="menubar-text" href="/admin">Admin</a></li>
{% endif %}
{% if is_super_teacher %}
<li class="flex-initial truncate menubar-item{% if current_page == 'super_teacher' %} active{% endif %}"><a class="menubar-text" href="/super-teacher">Super Teacher</a></li>
{% endif %}
<div class="flex-1"></div><!-- divider -->
{% if username %}
<!-- Logged in -->
Expand Down
31 changes: 31 additions & 0 deletions templates/public-adventures/body.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,37 @@
</div>
</div>

<div id="report" class="flex-1">
<div class="dropdown relative" data-type="single" id="report-select" data-value="{{selectedReport}}">
<button class="toggle-button font-semibold rounded inline-flex justify-between
appearance-none w-full border border-gray-200 text-gray-700 p-2 rounded"
onclick="$('#report_dropdown').slideToggle('medium');">
<span class="label" data-value="reported">{{selectedReported}}reported?</span>
<svg id="keyword-arrow_report" class="fill-current h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
</svg>
</button>
<div class="dropdown-menu dropdown-blue z-30 mt-2 w-full" id="report_dropdown"
style="display: none; width: 100%;"
_="on mutation of @style
set arrow to #keyword-arrow_report
if *display == 'none'
remove .rotate-180 from arrow
else if not arrow.classList.contains('rotate-180')
add .rotate-180 to arrow
end">
<div class="option {% if selected %}selected{% endif %}"
{% if selected %}
{% else %}
{% endif %}>reported</div>
<div class="option {% if selected %}selected{% endif %}"
{% if selected %}
{% else %}
{% endif %}>not reported</div>
</div>
</div>
</div>

</div>
</div>

Expand Down
10 changes: 10 additions & 0 deletions templates/super-teacher/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "auth.html" %}

{% block regular_content %}
<h1 class="text-center mb-10">Super teacher overview</h1>
<div class="flex flex-wrap gap-20 items-center justify-center">
<button class="green-btn p-4" id="users_button" onclick="window.open('/feedback', '_self')">Feedback overview</button>
<button class="green-btn p-4" onclick="window.open('/super-teacher/support', '_self')">Teacher support overview</button>
<button class="green-btn p-4" onclick="window.open('/public-adventures', '_self')">Public adventures overview</button>
</div>
{% endblock %}
Loading

0 comments on commit 3bdbe59

Please sign in to comment.