Compare commits

..

2 Commits

13 changed files with 317 additions and 254 deletions

View File

@ -11,14 +11,15 @@ Hui Lan <hui.lan@cantab.net>
EnglishPal allows the user to build his list of new English words
picked from articles selected for him to read according his vocabulary level. EnglishPal will determine a user's vocabulary level based on his picked words. After that, it will recommend articles for him to read, in order to booster his English vocabulary furthermore.
picked from articles selected for him according his vocabulary level.
## Run on your own laptop
## Run it on a local machine
`python3 main.py`
Make sure you have put the SQLite database file in the path `app/static` (see below).
Make sure you have the SQLite database file in `app/static` (see below).
## Run it as a Docker container
@ -28,32 +29,32 @@ Assuming that docker has been installed and that you are a sudo user (i.e., sudo
`sudo ./build.sh`
Open your favourite Internet browser and enter this URL address: `http://ip-address:90`. Note: you must update the variable `DEPLOYMENT_DIR` in `build.sh`.
Open your favourite Internet browser and enter this URL address: `http://ip-address:90`.
### Explanation on the commands in build.sh
My steps for deploying English on a Ubuntu server.
My steps for deploying English on the server.
- ssh to ubuntu@118.*.*.118
- cd to `/home/lanhui/englishpal2/EnglishPal`
- cd to /home/lanhui/englishpal2/EnglishPal
- Stop all docker service: `sudo service docker restart`. If you know the docker container ID, then the above command is an overkill. Use the following command instead: `sudo docker stop ContainerID`. You could get all container IDs with the following command: `sudo docker ps`
- Rebuild container. Run the following command to rebuild a docker image each time after the source code gets updated: `sudo docker build -t englishpal .`
- Rebuild container. Run the following command to rebuild a docker image after the code gets updated: `sudo docker build -t englishpal .`
- Run the application: `sudo docker run -d -p 90:80 -v /home/lanhui/englishpal2/EnglishPal/app/static/frequency:/app/static/frequency -t englishpal`. If you use `sudo docker run -d -p 90:80 -t englishpal`, data will be lost after terminating the program. If you want to automatically restart the docker image after each system reboot, add the option `--restart=always` after `docker run`.
- Run the application: `sudo docker run -d -p 90:80 -v /home/lanhui/englishpal2/EnglishPal/app/static/frequency:/app/static/frequency -t englishpal`. If you use `sudo docker run -d -p 90:80 -t englishpal`, data will be lost after terminating the program.
- Save disk space: `sudo docker system prune -a -f`
`build.sh` contains all the above commands. Run "sudo ./build.sh" to rebuild and start the web application.
- Save space: `sudo docker system prune -a -f`
#### Other useful docker commands
### Other useful docker commands
- `sudo docker ps -a`
- `sudo docker logs image_name`, where `image_name` could be obtained from `sudo docker ps`.
- `sudo docker logs image_name`, where image_name could be obtained from `sudo docker ps`.
`build.sh` contains all the above commands. Run "sudo ./build.sh" to rebuild and run the web application.
@ -67,10 +68,6 @@ All articles are stored in the `article` table in a SQLite file called
To add articles, open and edit `app/static/wordfreqapp.db` using DB Browser for SQLite (https://sqlitebrowser.org).
### Extending an account's expiry date
By default, an account's expiry is 30 days after first sign-up. To extend account's expiry date, open and edit `user` table in `app/static/wordfreqapp.db`. Simply update field `expiry_date`.
### Exporting the database
Export wordfreqapp.db to wordfreqapp.sql using the following commands:
@ -95,31 +92,33 @@ sqlite3 wordfreqapp.db`. Delete wordfreqapp.db first if it exists.
### Uploading wordfreqapp.db to the server
`pscp wordfreqapp.db lanhui@118.*.*.118:/home/lanhui/englishpal2/EnglishPal/app/static`
`pscp wordfreqapp.db lanhui@118.*.*.118:/home/lanhui/englishpal/app/static`
## Feedback
We welcome feedback on EnglishPal. Feedback examples:
We welcome feedback on EnglishPal.
### Feedback 1
- "Need a phone app. I use phone a lot. You cannot ask students to use computers."
### Respondent 1
### Feedback 2
"Need a phone app. I use phone a lot. You cannot ask students to use computers."
Can take a picture for text. Automatic translation.
### Respondent 2
- “成为会员”改成“注册”
“成为会员”改成“注册”
- “登出”改成“退出”
“登出”改成“退出”
- “收集生词吧”改成“生词收集栏”
“收集生词吧”改成“生词收集栏”
- 不要自动显示下一篇
“不要自动显示下一篇”
- 需要有“上一篇”、“下一篇”按钮。
需要有“上一篇”、“下一篇”
@ -138,7 +137,7 @@ EnglishPal's bugs and improvement suggestions are recorded in [Bugzilla](http://
- Usability testing
## Improvements made by contributors (incomplete list)
## Improvements made by contributors
### 朱文绮
@ -160,6 +159,7 @@ too many words that they already know, on the other hand, it can
reduce unnecessary memory occupied by the database, in addition, it
can also improve the simplicity of the page.
More information at: http://118.25.96.118/kanboard/?controller=TaskViewController&action=readonly&task_id=736&token=81a561da57ff7a172da17a480f0d421ff3bc69efbd29437daef90b1b8959
### 占健豪
@ -180,15 +180,5 @@ Demo video link: https://b23.tv/QuB77m
Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=215
### 丁锐
修复了以下漏洞
漏洞:新用户在创建账号时,不需要输入确定密码也可以注册成功,并且新账户可以正常使用。
Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=489
*Last modified on 2023-01-30*
### 娇娇
*Last modified on 2021-10-17*

View File

@ -1,6 +1,6 @@
import hashlib
import string
from datetime import datetime, timedelta
from datetime import datetime
from UseSqlite import InsertQuery, RecordQuery
path_prefix = '/var/www/wordfreq/wordfreq/'
@ -23,7 +23,7 @@ def verify_user(username, password):
def add_user(username, password):
start_date = datetime.now().strftime('%Y%m%d')
expiry_date = (datetime.now() + timedelta(days=30)).strftime('%Y%m%d') # will expire after 30 days
expiry_date = '20221230'
# 将用户名和密码一起加密,以免暴露不同用户的相同密码
password = md5(username + password)
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')

View File

@ -5,6 +5,7 @@ from Login import check_username_availability, verify_user, add_user, get_expiry
# 初始化蓝图
accountService = Blueprint("accountService", __name__)
### Sign-up, login, logout ###
@accountService.route("/signup", methods=['GET', 'POST'])
def signup():
@ -19,7 +20,6 @@ def signup():
# POST方法需判断是否注册成功再根据结果返回不同的内容
username = escape(request.form['username'])
password = escape(request.form['password'])
password2 = escape(request.form['password2'])
#! 添加如下代码为了过滤注册时的非法字符
warn = WarningMessage(username)
@ -32,8 +32,6 @@ def signup():
return render_template('signup.html')
elif len(password.strip()) < 4: # 密码过短
return '密码过于简单。'
elif password != password2:
return '确认密码与输入密码不一致!'
else: # 添加账户信息
add_user(username, password)
verified = verify_user(username, password)
@ -50,7 +48,6 @@ def signup():
return '用户名密码验证失败。'
@accountService.route("/login", methods=['GET', 'POST'])
def login():
'''

View File

@ -1,5 +0,0 @@
This folder holds users' vocabulary files.
Each file ends with .pickle.
For example, mrlan.pickle is the vocabulary file for user mrlan.

View File

@ -22,21 +22,13 @@ function getWord() {
function highLight() {
if (!isHighlight) return;
let articleContent = document.getElementById("article").innerText; //将原来的.innerText改为.innerHtml使用innerText会把原文章中所包含的<br>标签去除,导致处理后的文章内容失去了原来的格式
let articleContent = document.getElementById("article").innerText;
let pickedWords = document.getElementById("selected-words"); // words picked to the text area
let dictionaryWords = document.getElementById("selected-words2"); // words appearing in the user's new words list
let allWords = ""; //初始化allWords的值避免进入判断后编译器认为allWords未初始化的问题
if(dictionaryWords != null){//增加一个判断检查生词本里面是否为空如果为空allWords只添加选中的单词
allWords = pickedWords.value + " " + dictionaryWords.value;
}
else{
allWords = pickedWords.value + " ";
}
const list = allWords.split(" ");//将所有的生词放入一个list中用于后续处理
let allWords = pickedWords.value + " " + dictionaryWords.value;
const list = allWords.split(" ");
for (let i = 0; i < list.length; ++i) {
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, ""); //消除单词两边的空字符
list[i] = list[i].replace('|', "");
list[i] = list[i].replace('?', "");
if (list[i] !== "" && "<mark>".indexOf(list[i]) === -1 && "</mark>".indexOf(list[i]) === -1) {
//将文章中所有出现该单词word的地方改为" <mark>" + word + "<mark> "。 正则表达式RegExp()中,"\\s"代表单词前后必须要有空格,以防止只对单词中的部分字符高亮的情况出现。
articleContent = articleContent.replace(new RegExp("\\s"+list[i]+"\\s", "g"), " <mark>" + list[i] + "</mark> ");
@ -46,15 +38,15 @@ function highLight() {
}
function cancelHighlighting() {
let articleContent = document.getElementById("article").innerText;//将原来的.innerText改为.innerHtml原因同上
let articleContent = document.getElementById("article").innerText;
let pickedWords = document.getElementById("selected-words");
const dictionaryWords = document.getElementById("selected-words2");
const list = pickedWords.value.split(" ");
if (pickedWords != null) {
for (let i = 0; i < list.length; ++i) {
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, "");
if (list[i] !== "") { //原来判断的代码中替换的内容为“list[i]”这个字符串这明显是错误的我们需要替换的是list[i]里的内容
articleContent = articleContent.replace(new RegExp("<mark>"+list[i]+"</mark>", "g"), list[i]);
if (list[i] !== "") {
articleContent = articleContent.replace("<mark>" + list[i] + "</mark>", "list[i]");
}
}
}
@ -63,8 +55,8 @@ function cancelHighlighting() {
for (let i = 0; i < list2.length; ++i) {
list2 = dictionaryWords.value.split(" ");
list2[i] = list2[i].replace(/(^\s*)|(\s*$)/g, "");
if (list2[i] !== "") { //原来代码中替换的内容为“list[i]”这个字符串这明显是错误的我们需要替换的是list[i]里的内容
articleContent = articleContent.replace(new RegExp("<mark>"+list2[i]+"</mark>", "g"), list2[i]);
if (list2[i] !== "") {
articleContent = articleContent.replace("<mark>" + list[i] + "</mark>", "list[i]");
}
}
}

View File

@ -7,20 +7,10 @@ function familiar(theWord) {
url:"/" + username + "/" + word + "/familiar",
success:function(response){
let new_freq = freq - 1;
const allow_move = document.getElementById("move_dynamiclly").checked;
if (allow_move) {
if (new_freq <= 0) {
removeWord(theWord);
} else {
renderWord({ word: theWord, freq: new_freq });
}
if(new_freq <1) {
$("#p_" + theWord).remove();
} else {
if(new_freq <1) {
$("#p_" + theWord).remove();
} else {
$("#freq_" + theWord).text(new_freq);
}
$("#freq_" + theWord).text(new_freq);
}
}
});
@ -35,139 +25,19 @@ function unfamiliar(theWord) {
url:"/" + username + "/" + word + "/unfamiliar",
success:function(response){
let new_freq = parseInt(freq) + 1;
const allow_move = document.getElementById("move_dynamiclly").checked;
if (allow_move) {
renderWord({ word: theWord, freq: new_freq });
} else {
$("#freq_" + theWord).text(new_freq);
}
$("#freq_" + theWord).text(new_freq);
}
});
}
function delete_word(theWord) {
let username = $("#username").text();
let word = theWord.replace('&amp;', '&');
let word = $("#word_" + theWord).text();
$.ajax({
type:"GET",
url:"/" + username + "/" + word + "/del",
success:function(response){
const allow_move = document.getElementById("move_dynamiclly").checked;
if (allow_move) {
removeWord(theWord);
} else {
$("#p_" + theWord).remove();
}
$("#p_" + theWord).remove();
}
});
}
/*
* interface Word {
* word: string,
* freq: number
* }
* */
/**
* 传入一个词频HTML元素将其解析为Word类型的对象
*/
function parseWord(element) {
const word = element
.querySelector("a.btn.btn-light[role=button]") // 获取当前词频元素的词汇元素
.innerText // 获取词汇值;
const freq = Number.parseInt(element.querySelector(`#freq_${word}`).innerText); // 获取词汇的数量
return {
word,
freq
};
}
/**
* 使用模板将传入的单词转换为相应的HTML字符串
*/
function wordTemplate(word) {
// 这个模板应当与 templates/userpage_get.html 中的 <p id='p_${word.word}' class="new-word" > ... </p> 保持一致
return `<p id='p_${word.word}' class="new-word" >
<a id="word_${word.word}" class="btn btn-light" href='http://youdao.com/w/eng/${word.word}/#keyfrom=dict2.index'
role="button">${word.word}</a>
( <a id="freq_${word.word}" title="${word.word}">${word.freq}</a> )
<a class="btn btn-success" onclick="familiar('${word.word}')" role="button">熟悉</a>
<a class="btn btn-warning" onclick="unfamiliar('${word.word}')" role="button">不熟悉</a>
<a class="btn btn-danger" onclick="delete_word('${word.word}')" role="button">删除</a>
</p>`;
}
/**
* 删除某一词频元素
* 此处word为词频元素对应的单词
*/
function removeWord(word) {
// 根据词频信息删除元素
word = word.replace('&amp;', '&');
const element_to_remove = document.getElementById(`p_${word}`);
if (element_to_remove != null) {
element_to_remove.remove();
}
}
function renderWord(word) {
const container = document.querySelector(".word-container");
// 删除原有元素
removeWord(word.word);
// 插入新元素
let inserted = false;
const new_element = elementFromString(wordTemplate(word));
for (const current of container.children) {
const cur_word = parseWord(current);
// 找到第一个词频比它小的元素,插入到这个元素前面
if (compareWord(cur_word, word) == -1) {
container.insertBefore(new_element, current);
inserted = true;
break;
}
}
// 当word就是词频最小的词时把他补回去
if (!inserted) {
container.appendChild(new_element);
}
// 让发生变化的元素抖动
new_element.classList.add("shaking");
// 移动到该元素
new_element.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"});
// 抖动完毕后删除抖动类
setTimeout(() => {
new_element.classList.remove("shaking");
}, 1600);
}
/**
* 从string中创建一个HTML元素并返回
*/
function elementFromString(string) {
const d = document.createElement('div');
d.innerHTML = string;
return d.children.item(0);
}
/**
* 对比两个单词
* 当first小于second时返回-1
* 当first等于second时返回0
* 当first大于second时返回1
*/
function compareWord(first, second) {
if (first.freq < second.freq) {
return -1;
}
if (first.freq > second.freq) {
return 1;
}
if (first.word < second.word) {
return -1;
}
if (first.word > second.word) {
return 1;
}
return 0;
}

View File

@ -5,7 +5,7 @@
<title>账号过期</title>
</head>
<body>
<p>您的账号过期(过期日 {{expiry_date}}</p>
<p>您的账号{{ username }}过期</p>
<p>为了提高服务质量English Pal 收取会员费用, 每天1元。</p>
<p>请决定你要试用的时间长度,扫描下面支付宝二维码支付。 支付时请注明<i>English Pal Membership Fee</i>。 我们会于12小时内激活账号。</p>
<p><img src="static/donate-the-author-hidden.jpg" width="120px" alt="支付宝二维码" /></p>

View File

@ -17,8 +17,8 @@ You're logged in already! <a href="/logout">Logout</a>.
<form action="/signup" method="POST">
<p><input type="username" name="username" placeholder="输入用户名" required="required" class="username"></p>
<p><input type="password" name="password" placeholder="输入密码" required="required" class="password"></p>
<p><input type="password" name="password2" placeholder="确认密码" required="required" class="password" ></p>
<p><input type="password" name="password" placeholder="输入密码" class="password"></p>
<p><input type="password" name="password2" placeholder="确认密码" class="password"></p>
<button type="submit" class="btn">注册</button>
</form>

View File

@ -19,33 +19,19 @@
{% endif %}
<title>EnglishPal Study Room for {{ username }}</title>
<style>
.shaking {
animation: shakes 1600ms ease-in-out;
}
@keyframes shakes {
10%, 90% { transform: translate3d(-1px, 0, 0); }
20%, 50% { transform: translate3d(+2px, 0, 0); }
30%, 70% { transform: translate3d(-4px, 0, 0); }
40%, 60% { transform: translate3d(+4px, 0, 0); }
50% { transform: translate3d(-4px, 0, 0); }
}
</style>
</head>
<body>
<div class="container-fluid">
<p><b>English Pal for <font id="username" color="red">{{ username }}</font></b>
<a class="btn btn-secondary" href="/logout" role="button">退出</a>
<a class="btn btn-secondary" href="/reset" role="button">重设密码</a>
<a class="btn btn-secondary" href="/logout" role="button" onclick="stopRead()">退出</a>
<a class="btn btn-secondary" href="/reset" role="button" onclick="stopRead()">重设密码</a>
</p>
{{ flashed_messages|safe }}
<a class="btn btn-success" href="/{{ username }}/reset" role="button"> 下一篇 Next Article </a>
<a class="btn btn-success" href="/{{ username }}/reset" role="button" onclick="stopRead()"> 下一篇 Next Article </a>
{% if session.get('articleID') != session.get('old_articleID') %}
{% if session.get('old_articleID') != None %}
<a class="btn btn-success" href="/{{ username }}/back" role="button"> 上一篇 Previous Article </a>
<a class="btn btn-success" href="/{{ username }}/back" role="button" onclick="stopRead()"> 上一篇 Previous Article </a>
{% endif%}
{% endif %}
@ -66,7 +52,7 @@
<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>
<form method="post" action="/{{ username }}">
<textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/>
<input type="submit" value="把生词加入我的生词库"/>
<input type="submit" onclick="stopRead()" value="把生词加入我的生词库"/>
<input type="reset" value="清除"/>
</form>
{% if session.get['thisWord'] %}
@ -81,30 +67,22 @@
{% endif %}
{% if d_len > 0 %}
<p>
<b>我的生词簿</b>
<label for="move_dynamiclly">
<input type="checkbox" name="move_dynamiclly" id="move_dynamiclly" checked>
允许动态调整顺序
</label>
</p>
<a name="aaa"></a>
<div class="word-container">
{% for x in lst3 %}
{% set word = x[0] %}
{% set freq = x[1] %}
{% if session.get('thisWord') == x[0] and session.get('time') == 1 %}
{% endif %}
<p id='p_{{ word }}' class="new-word" >
<a id="word_{{ word }}" class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
role="button">{{ word }}</a>
( <a id="freq_{{ word }}" title="{{ word }}">{{ freq }}</a> )
<a class="btn btn-success" onclick="familiar('{{ word }}')" role="button">熟悉</a>
<a class="btn btn-warning" onclick="unfamiliar('{{ word }}')" role="button">不熟悉</a>
<a class="btn btn-danger" onclick="delete_word('{{ word }}')" role="button">删除</a>
</p>
{% endfor %}
</div>
<p><b>我的生词簿</b></p>
{% for x in lst3 %}
{% set word = x[0] %}
{% set freq = x[1] %}
{% if session.get('thisWord') == x[0] and session.get('time') == 1 %}
<a name="aaa"></a>
{% endif %}
<p id='p_{{ word }}' class="new-word" >
<a id="word_{{ word }}" class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
role="button">{{ word }}</a>
( <a id="freq_{{ word }}" title="{{ word }}">{{ freq }}</a> )
<a class="btn btn-success" onclick="familiar('{{ word }}')" role="button">熟悉</a>
<a class="btn btn-warning" onclick="unfamiliar('{{ word }}')" role="button">不熟悉</a>
<a class="btn btn-danger" onclick="delete_word('{{ word }}')" role="button">删除</a>
</p>
{% endfor %}
<input id="selected-words2" type="hidden" value="{{ words }}">
{% endif %}
</div>

View File

@ -20,7 +20,7 @@
<title>EnglishPal Study Room for {{username}}</title>
</head>
<body>
<p>取消勾选认识的单词</p>
<p>勾选认识的单词</p>
<form method="post" action="/{{username}}/mark">
<input type="submit" name="add-btn" value="加入我的生词簿"/>
{% for x in lst %}
@ -30,7 +30,7 @@
:
<a href='http://youdao.com/w/eng/{{word}}/#keyfrom=dict2.index' title={{word}}>{{word}}</a>
({{x[1]}})
<input type="checkbox" name="marked" value="{{word}}" checked>
<input type="checkbox" name="marked" value="{{word}}">
</p>
{% endfor %}

View File

@ -107,7 +107,7 @@ def userpage(username):
# 用户过期
user_expiry_date = session.get('expiry_date')
if datetime.now().strftime('%Y%m%d') > user_expiry_date:
return render_template('expiry.html', expiry_date=user_expiry_date)
return render_template('expiry.html')
# 获取session里的用户名
username = session.get('username')

View File

@ -39,7 +39,7 @@ def file2str(fname):#文件转字符
def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用时才给s赋值。
special_characters = '\_©~<=>+-/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉
special_characters = '_©~=+[]*&$%^@.,?!:;#()"“”—‘’' # 把里面的字符都去掉
for c in special_characters:
s = s.replace(c, ' ') # 防止出现把 apple,apple 移掉逗号后变成 appleapple 情况
s = s.replace('--', ' ')

241
v Normal file
View File

@ -0,0 +1,241 @@
commit 671df67723abfe723fa51400f705e71d7e9332e5 (HEAD -> SPM2022F-CONTRIBUTORS-JIAOJIAO, origin/master, origin/HEAD, master)
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Thu Nov 10 19:03:59 2022 +0800
Bug487-WuYuhan-Refactor (#58)
将所有用于用户名验证的逻辑放入到 `UserName` 类中。
Hui
Co-authored-by: Lan Hui <1348141770@qq.com>
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/58
Co-authored-by: mrlan <mrlan@noreply.121.4.94.30>
Co-committed-by: mrlan <mrlan@noreply.121.4.94.30>
commit f909201615500b8df1d296cae72964b06980cd6f
Merge: bfd87c5 29ffada
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Tue Nov 8 19:53:09 2022 +0800
Merge pull request 'Bug487-WuYuhan-Refactor' (#57) from Bug487-WuYuhan-Refactor into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/57
commit 29ffada7eb442c19406fa2cc6f8dd1eb6dff2466
Author: Hui Lan <lanhui@zjnu.edu.cn>
Date: Thu Nov 3 22:28:25 2022 +0800
Login.py: improve comments.
commit d3a796428d9eac9ebe2f35329540dff34d7fb06b
Author: Hui Lan <lanhui@zjnu.edu.cn>
Date: Thu Nov 3 22:21:34 2022 +0800
account_service.py: module re is no longer necessary.
commit 702205940cc8962f23b304c55b13ccbb8a0d6639
Author: Lan Hui <1348141770@qq.com>
Date: Thu Nov 3 22:06:24 2022 +0800
Login.py: must convert warn to string before comparing to OK
commit f0b5adc5e4fc25298042a02efc63f61aa9830ded
Author: Lan Hui <1348141770@qq.com>
Date: Thu Nov 3 22:02:32 2022 +0800
Login.py: fix function name
commit 3cfec31c3fe739d679c2af3d1e1bbaf3e5826896
Author: Hui Lan <lanhui@zjnu.edu.cn>
Date: Thu Nov 3 22:00:47 2022 +0800
Login.py: add missing colon
commit 286e884dd8a08fe3bb14cd9f656c2da1f70af2b5
Author: Hui Lan <lanhui@zjnu.edu.cn>
Date: Thu Nov 3 21:59:12 2022 +0800
Refactor Wu Yuhan's code
commit bfd87c51f6755c600f5656fd4bcfb4babf708ab9
Merge: ab01b8e 59d95d8
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Thu Nov 3 21:28:55 2022 +0800
Merge pull request 'Bug487-WuYuhan' (#56) from Bug487-WuYuhan into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/56
commit ab01b8e19be89dd7d8a29c3ead4368c48c9f0481
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Tue Nov 1 21:43:01 2022 +0800
Hui-Build (#55)
Make English Pal docker run automatically each time after Ubuntu reboot.
Hui
Co-authored-by: Hui Lan <lanhui@zjnu.edu.cn>
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/55
Co-authored-by: mrlan <mrlan@noreply.121.4.94.30>
Co-committed-by: mrlan <mrlan@noreply.121.4.94.30>
commit 59d95d8e9fbd7ea48f43e4296cd8223c992d77d3 (origin/Bug487-WuYuhan)
Author: wuyuhan <woodwhale@qq.com>
Date: Fri Oct 21 11:07:20 2022 +0800
account_service.py: 导入re库使用正则匹配过滤了注册时用户名的非法字符
commit 5844eab6d5d15a05f825e3cfd9c5e5905b0b8a8f
Author: woodwhale <woodwhale@qq.com>
Date: Fri Oct 21 10:44:39 2022 +0800
account_service.py: 添加注册时用户名的非法字符过滤
commit 02ffcd3b592b7a12e21ee5e016175150d24a4cb8
Merge: 9927515 ecc354b
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Fri Aug 5 16:18:06 2022 +0800
Merge pull request 'Bug412-JiangLetian-Refactor' (#54) from Bug412-JiangLetian-Refactor into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/54
commit ecc354bc0dc4e0b1fb0fc3845da8f54c34bd7ed7 (origin/Bug412-JiangLetian-Refactor)
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 12:33:41 2022 +0800
Refactor: use better function
commit 1d8671c5c78768c29dab349670cbcbbb88f5fda5
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 12:30:27 2022 +0800
Refactor: use better function name
commit 8cb34e56ba752bab2a18d1f33a3beb2ee5e74363
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 12:26:18 2022 +0800
Refactor: remove duplicate code block
commit b5dacb9ad21a932c80d891d208184606dcc8ec7b
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 11:52:40 2022 +0800
Improve comments
commit 47e745e7742658c10c4b201b92719e690fd35b27
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 11:45:21 2022 +0800
Use better variable name (use articleContent instead of txt, and use camelCase)
commit 1dfe370983a7a8112b278f5bfce5fd5587104c49
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 11:39:35 2022 +0800
Use better variable names
commit 9927515b1139de5f2c0c42a6b28f9a3efa4897c1
Merge: b745da4 c15746b
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Tue Aug 2 11:03:39 2022 +0800
Merge pull request 'Bug412-JiangLetian' (#39) from Bug412-JiangLetian into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/39
commit c15746bbb2c663bcdb585ac541c6c270e25a8019 (origin/Bug412-JiangLetian)
Merge: b745da4 041cbd9
Author: Lan Hui <1348141770@qq.com>
Date: Tue Aug 2 11:00:33 2022 +0800
Resolve conflicts
commit b745da4c908a600a734dec08ba5373ae838cdace
Merge: 7663dfb 4817557
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Sun Jul 31 09:11:53 2022 +0800
Merge pull request 'IMPROVE-WangWeiLong' (#35) from IMPROVE-WangWeiLong into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/35
commit 7663dfb8f48eece671a89ae8c252571b91028042
Merge: 2c1bc98 0098fa8
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Fri Jul 29 16:20:01 2022 +0800
Merge pull request 'Hui-EscapeUserInput' (#53) from Hui-EscapeUserInput into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/53
commit 0098fa87469f918f2c4a84f5ef49031e8e63c4f0 (origin/Hui-EscapeUserInput)
Author: Lan Hui <1348141770@qq.com>
Date: Fri Jul 29 15:26:19 2022 +0800
Prevent attribute injection
commit 828cef406ccc4996925359650312d3dc41625100
Author: Lan Hui <1348141770@qq.com>
Date: Fri Jul 29 15:22:42 2022 +0800
Escape user input first
commit 2c1bc98833e2427aab810e23248a9476e85bc030
Author: 徐幸 <2567198082@qq.com>
Date: Thu Jul 21 23:13:33 2022 +0800
Bug422-XuXing (#46)
增加了返回上一篇的按钮及相关功能的实现,当点击下一篇文章跳转至下一篇时,页面中会增加一个返回上一篇按钮,点击返回上一篇按钮后可以回到上一篇。
Co-authored-by: Lan Hui <1348141770@qq.com>
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/46
Co-authored-by: 徐幸 <2567198082@qq.com>
Co-committed-by: 徐幸 <2567198082@qq.com>
commit 9a89510f4effb9388bff2b831bd2482a16a5016f
Merge: 028e2f9 e9eb604
Author: mrlan <mrlan@noreply.121.4.94.30>
Date: Wed Jul 20 17:31:01 2022 +0800
Merge pull request 'Improvement-Stewart' (#49) from Improvement-Stewart into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/49
commit e9eb604a2255d563b61b56ed7f45f439255e8935 (origin/Improvement-Stewart)
Author: Lan Hui <1348141770@qq.com>
Date: Wed Jul 20 17:10:03 2022 +0800
Improve spacing and indentation.
commit 9beb1ad1d2fb5df12dffff1f6f990fd12cf891a7
Author: Lan Hui <1348141770@qq.com>
Date: Wed Jul 20 17:09:04 2022 +0800
Make up the enclosing >.
commit 8998d6e4afedd85da68a6d74cbf9213251331486
Author: Lan Hui <1348141770@qq.com>
Date: Wed Jul 20 17:08:07 2022 +0800
Improve spacing.
commit 8747f35fd8f1dd40400c44ed9ed955e5c1cce6ae
Author: Lan Hui <1348141770@qq.com>
Date: Wed Jul 20 17:07:09 2022 +0800
Make up the enclosing >.
commit fdb432031f74ab2955d1ee4bb9089a70d2e5f7fd
Author: Lan Hui <1348141770@qq.com>
Date: Wed Jul 20 17:06:11 2022 +0800
Better indentation.
commit 1401870591b479731f54e41eb4815466e4a109c3
Author: Lan Hui <1348141770@qq.com>