1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-02 23:26:23 -07:00

Fix condition double-sending

This commit is contained in:
Eggbertx 2024-08-19 16:34:48 -07:00
parent a7744d6d88
commit 38c4107b15
3 changed files with 112 additions and 46 deletions

View file

@ -188,24 +188,19 @@ func (f *Filter) Conditions() ([]FilterCondition, error) {
return f.conditions, rows.Close() return f.conditions, rows.Close()
} }
// SetConditions replaces all current conditions associated with the filter and applies the given conditions. func (f *Filter) setConditionsContext(ctx context.Context, tx *sql.Tx, conditions ...FilterCondition) error {
// It returns an error if no conditions are provided if f.ID == 0 {
func (f *Filter) SetConditions(conditions ...FilterCondition) error { return ErrInvalidFilter
if len(conditions) < 1 { }
if len(conditions) == 0 {
return ErrNoConditions return ErrNoConditions
} }
ctx, cancel := context.WithTimeout(context.Background(), gcdb.defaultTimeout)
defer cancel()
tx, err := BeginContextTx(ctx) _, err := ExecContextSQL(ctx, tx, `DELETE FROM DBPREFIXfilter_conditions WHERE filter_id = ?`, f.ID)
if err != nil { if err != nil {
return err return err
} }
defer tx.Rollback()
if _, err = ExecContextSQL(ctx, tx, `DELETE FROM DBPREFIXfilter_conditions WHERE filter_id = ?`, f.ID); err != nil {
return err
}
for c, condition := range conditions { for c, condition := range conditions {
conditions[c].FilterID = f.ID conditions[c].FilterID = f.ID
condition.FilterID = f.ID condition.FilterID = f.ID
@ -213,20 +208,59 @@ func (f *Filter) SetConditions(conditions ...FilterCondition) error {
return err return err
} }
} }
if err = tx.Commit(); err != nil {
return err
}
f.conditions = conditions f.conditions = conditions
return nil return nil
} }
func (f *Filter) UpdateDetails(staffNote string, matchAction string, matchDetail string) error { // SetConditions replaces all current conditions associated with the filter and applies the given conditions.
_, err := ExecTimeoutSQL(nil, // It returns an error if no conditions are provided
func (f *Filter) SetConditions(conditions ...FilterCondition) error {
ctx, cancel := context.WithTimeout(context.Background(), gcdb.defaultTimeout)
defer cancel()
tx, err := BeginContextTx(ctx)
if err != nil {
return err
}
defer tx.Rollback()
if err = f.setConditionsContext(ctx, tx, conditions...); err != nil {
return err
}
return tx.Commit()
}
func (f *Filter) updateDetailsContext(ctx context.Context, tx *sql.Tx, staffNote string, matchAction string, matchDetail string) error {
_, err := ExecContextSQL(ctx, tx,
`UPDATE DBPREFIXfilters SET staff_note = ?, issued_at = ?, match_action = ?, match_detail = ? WHERE id = ?`, `UPDATE DBPREFIXfilters SET staff_note = ?, issued_at = ?, match_action = ?, match_detail = ? WHERE id = ?`,
staffNote, time.Now(), matchAction, matchDetail, f.ID, staffNote, time.Now(), matchAction, matchDetail, f.ID,
) )
if err != nil {
return err return err
} }
f.StaffNote = staffNote
f.MatchAction = matchAction
f.MatchDetail = matchDetail
return nil
}
// UpdateDetails updates the filter's staff note, match action, and match detail (ban message, reject reason, etc)
func (f *Filter) UpdateDetails(staffNote string, matchAction string, matchDetail string) error {
if f.ID == 0 {
return ErrInvalidFilter
}
ctx, cancel := context.WithTimeout(context.Background(), gcdb.defaultTimeout)
defer cancel()
tx, err := BeginContextTx(ctx)
if err != nil {
return err
}
defer tx.Rollback()
if err = f.updateDetailsContext(ctx, tx, staffNote, matchAction, matchDetail); err != nil {
return err
}
return tx.Commit()
}
// BoardDirs returns an array of board directories associated with this filter // BoardDirs returns an array of board directories associated with this filter
func (f *Filter) BoardDirs() ([]string, error) { func (f *Filter) BoardDirs() ([]string, error) {
@ -307,6 +341,21 @@ func (f *Filter) BoardIDs() ([]int, error) {
return ids, nil return ids, nil
} }
func (f *Filter) setBoardIDsContext(ctx context.Context, tx *sql.Tx, ids ...int) error {
_, err := ExecContextSQL(ctx, tx, `DELETE FROM DBPREFIXfilter_boards WHERE filter_id = ?`, f.ID)
if err != nil {
return err
}
for _, boardID := range ids {
if _, err = ExecContextSQL(ctx, tx,
`INSERT INTO DBPREFIXfilter_boards(filter_id, board_id) VALUES (?,?)`, f.ID, boardID,
); err != nil {
return err
}
}
return nil
}
// SetBoardIDs sets the board IDs to be associated with the filter. If no boards are used, // SetBoardIDs sets the board IDs to be associated with the filter. If no boards are used,
// the filter will be applied to all boards // the filter will be applied to all boards
func (f *Filter) SetBoardIDs(ids ...int) error { func (f *Filter) SetBoardIDs(ids ...int) error {
@ -318,17 +367,9 @@ func (f *Filter) SetBoardIDs(ids ...int) error {
} }
defer tx.Rollback() defer tx.Rollback()
if _, err = ExecContextSQL(ctx, tx, `DELETE FROM DBPREFIXfilter_boards WHERE filter_id = ?`, f.ID); err != nil { if err = f.setBoardIDsContext(ctx, tx, ids...); err != nil {
return err return err
} }
for _, boardID := range ids {
if _, err = ExecContextSQL(ctx, tx,
`INSERT INTO DBPREFIXfilter_boards(filter_id, board_id) VALUES (?,?)`,
f.ID, boardID,
); err != nil {
return err
}
}
return tx.Commit() return tx.Commit()
} }
@ -487,6 +528,20 @@ func (fc *FilterCondition) testCondition(post *Post, upload *Upload, request *ht
return match, err return match, err
} }
// CanDoRegex is a convenience function for templates. It returns true if the filter condition should show a regular expression
// checkbox
func (fc FilterCondition) CanDoRegex() bool {
return fc.HasSearchField() && (fc.Field == "name" || fc.Field == "trip" || fc.Field == "email" || fc.Field == "subject" ||
fc.Field == "body" || fc.Field == "filename" || fc.Field == "useragent")
}
// HasSearchField is a convenience function for templates. It returns true if the filter condition should show a search box
func (fc FilterCondition) HasSearchField() bool {
return fc.Field != "firsttimeboard" && fc.Field != "notfirsttimeboard" && fc.Field != "firsttimesite" &&
fc.Field != "notfirsttimesite" && fc.Field != "isop" && fc.Field != "notop" && fc.Field != "hasfile" &&
fc.Field != "nofile"
}
// DoPostFiltering checks the filters against the given post. If a match is found, its respective action is taken and the filter // DoPostFiltering checks the filters against the given post. If a match is found, its respective action is taken and the filter
// is returned. It logs any errors it receives and returns a sanitized error (if one occured) that can be shown to the end user // is returned. It logs any errors it receives and returns a sanitized error (if one occured) that can be shown to the end user
func DoPostFiltering(post *Post, upload *Upload, boardID int, request *http.Request, errEv *zerolog.Event) (*Filter, error) { func DoPostFiltering(post *Post, upload *Upload, boardID int, request *http.Request, errEv *zerolog.Event) (*Filter, error) {
@ -555,12 +610,15 @@ func ApplyFilter(filter *Filter, conditions []FilterCondition, boards []int) err
if err = QueryRowContextSQL(ctx, tx, `SELECT MAX(id) FROM DBPREFIXfilters`, nil, []any{&filter.ID}); err != nil { if err = QueryRowContextSQL(ctx, tx, `SELECT MAX(id) FROM DBPREFIXfilters`, nil, []any{&filter.ID}); err != nil {
return err return err
} }
} else {
filter.updateDetailsContext(ctx, tx, filter.StaffNote, filter.MatchAction, filter.MatchDetail)
} }
if err = tx.Commit(); err != nil {
if err = filter.setConditionsContext(ctx, tx, conditions...); err != nil {
return err return err
} }
if err = filter.SetConditions(conditions...); err != nil { if err = filter.setBoardIDsContext(ctx, tx, boards...); err != nil {
return err return err
} }
return filter.SetBoardIDs(boards...) return tx.Commit()
} }

View file

@ -252,9 +252,6 @@ func filtersCallback(_ http.ResponseWriter, request *http.Request, staff *gcsql.
errEv.Err(err).Caller().Int("filterID", filterID).Msg("Unable to get filter from ID") errEv.Err(err).Caller().Int("filterID", filterID).Msg("Unable to get filter from ID")
return nil, err return nil, err
} }
if conditions, err = filter.Conditions(); err != nil {
errEv.Err(err).Caller().Int("filterID", filterID).Msg("Unable to get filter conditions list")
}
} }
for k, v := range request.PostForm { for k, v := range request.PostForm {
@ -271,7 +268,6 @@ func filtersCallback(_ http.ResponseWriter, request *http.Request, staff *gcsql.
boardIDLogArr.Int(boardID) boardIDLogArr.Int(boardID)
boards = append(boards, boardID) boards = append(boards, boardID)
} }
infoEv.Array("boardIDs", boardIDLogArr)
// set filter conditions // set filter conditions
if strings.HasPrefix(k, "field") { if strings.HasPrefix(k, "field") {
@ -319,8 +315,10 @@ func filtersCallback(_ http.ResponseWriter, request *http.Request, staff *gcsql.
Msg("Unable to submit filter") Msg("Unable to submit filter")
return nil, err return nil, err
} }
infoEv.Msg("Filter submitted")
} }
data["filterBoards"] = make([]int, 0)
if editFilter := request.FormValue("edit"); editFilter != "" { if editFilter := request.FormValue("edit"); editFilter != "" {
// user clicked on Edit link in filter row // user clicked on Edit link in filter row
filterID, err := strconv.Atoi(editFilter) filterID, err := strconv.Atoi(editFilter)
@ -338,6 +336,10 @@ func filtersCallback(_ http.ResponseWriter, request *http.Request, staff *gcsql.
errEv.Err(err).Caller().Int("filterID", filterID).Msg("Unable to get filter conditions") errEv.Err(err).Caller().Int("filterID", filterID).Msg("Unable to get filter conditions")
return nil, errors.New("unable to get filter conditions") return nil, errors.New("unable to get filter conditions")
} }
if data["filterBoards"], err = filter.BoardIDs(); err != nil {
errEv.Err(err).Caller().Msg("Unable to get filter board IDs")
return nil, errors.New("unable to get filter board IDs")
}
} else { } else {
// user loaded /manage/filters, populate single "default" condition // user loaded /manage/filters, populate single "default" condition
data["filter"] = &gcsql.Filter{ data["filter"] = &gcsql.Filter{

View file

@ -5,23 +5,23 @@
<th>Field/Condition:</th> <th>Field/Condition:</th>
<td> <td>
<select name="field{{.conditionNo}}" class="sel-field"> <select name="field{{.conditionNo}}" class="sel-field">
{{- range $_,$field := .fields}}<option value="{{$field.Value}}" {{if eq $field.Value $.condition.Field}}selected{{end}}>{{$field.Text}}</option>{{end -}} {{- range $_,$field := .fields -}}
<option value="{{$field.Value}}" {{if eq $field.Value $.condition.Field}}selected{{end}}>{{$field.Text}}</option>
{{- end -}}
</select> </select>
</td> </td>
</tr> </tr>
<tr class="search-cndtn"> <tr class="search-cndtn" {{if not .condition.HasSearchField}}style="display:none"{{end}}>
<th>Search:</th> <th>Search:</th>
<td><input type="text" name="search{{.conditionNo}}" value="{{.condition.Search}}"></td> <td><input type="text" name="search{{.conditionNo}}" value="{{.condition.Search}}"></td>
</tr> </tr>
<tr class="regex-cndtn"> <tr class="regex-cndtn" {{if not .condition.CanDoRegex}}style="display: none"{{end}}>
<th></th> <th></th>
<td><label>Is regex <input type="checkbox" name="isregex{{.conditionNo}}" {{if .condition.IsRegex}}checked{{end}}/></label></td> <td><label>Is regex <input type="checkbox" name="isregex{{.conditionNo}}" {{if .condition.IsRegex}}checked{{end}}/></label></td>
</tr> </tr>
<tr class="btns-cndtn"> <tr class="btns-cndtn">
<td></td> <td></td>
<td> <td><a href="#" class="rem-cndtn">Remove condition</a></td>
<a href="#" class="rem-cndtn">Remove condition</a>
</td>
</tr> </tr>
</table> </table>
</fieldset> </fieldset>
@ -29,7 +29,7 @@
<h2>{{if gt $.filter.ID 0}}Edit filter{{else}}New filter{{end}}</h2> <h2>{{if gt $.filter.ID 0}}Edit filter{{else}}New filter{{end}}</h2>
<form id="filterform" action="{{webPath `/manage/filters`}}{{if gt $.filter.ID 0}}?edit={{.ID}}{{end}}" method="POST"> <form id="filterform" action="{{webPath `/manage/filters`}}{{if gt $.filter.ID 0}}?edit={{.ID}}{{end}}" method="POST">
{{- if gt $.filter.ID 0}}<input type="hidden" name="filterid" value="{{.ID}}">{{end -}} {{- if gt $.filter.ID 0}}<input type="hidden" name="filterid" value="{{$.filter.ID}}">{{end -}}
Filter conditions are checked against a post after the IP is checked against the <a href="{{webPath `/manage/bans`}}">ban list</a>. Filter conditions are checked against a post after the IP is checked against the <a href="{{webPath `/manage/bans`}}">ban list</a>.
The filter action will be executed only if all conditions are met. Tripcode searches do not include the prefix "!". The filter action will be executed only if all conditions are met. Tripcode searches do not include the prefix "!".
<table> <table>
@ -53,31 +53,37 @@
<th>Action</th> <th>Action</th>
<td> <td>
<select name="action" id="action"> <select name="action" id="action">
<option value="reject">Reject post</option> <option value="reject" {{if eq $.filter.MatchAction `reject`}}selected{{end}}>Reject post</option>
<option value="ban">Ban IP</option> <option value="ban" {{if eq $.filter.MatchAction `ban`}}selected{{end}}>Ban IP</option>
<option value="log">Log match</option> <option value="log" {{if eq $.filter.MatchAction `log`}}selected{{end}}>Log match</option>
</select> </select>
</td> </td>
</tr> </tr>
<tr> <tr>
<th id="detail">Reason</th> <th id="detail">Reason</th>
<td><textarea name="detail" rows="5" cols="35">{{if gt $.filter.ID 0}}{{.MatchDetail}}{{end}}</textarea></td> <td><textarea name="detail" rows="5" cols="35">{{$.filter.MatchDetail}}</textarea></td>
</tr> </tr>
<tr> <tr>
<th id="note">Staff Note</th> <th id="note">Staff Note</th>
<td><textarea name="note" rows="5" cols="35">{{if gt $.filter.ID 0}}{{.StaffNote}}{{end}}</textarea></td> <td><textarea name="note" rows="5" cols="35">{{$.filter.StaffNote}}</textarea></td>
</tr> </tr>
<tr> <tr>
<th>Board(s):</th> <th>Board(s):</th>
<td id="boardslist"> <td id="boardslist">
If no boards are selected, the filter will be applied to all boards If no boards are selected, the filter will be applied to all boards
{{- range $_,$board := .allBoards -}} {{- range $_,$board := .allBoards -}}
<label for="applyboard{{$board.ID}}">/{{$board.Dir}}/ - {{$board.Title}} <input type="checkbox" name="applyboard{{$board.ID}}" id="applyboard{{$board.ID}}"></label> <label for="applyboard{{$board.ID}}">/{{$board.Dir}}/ - {{$board.Title}} <input type="checkbox" name="applyboard{{$board.ID}}" id="applyboard{{$board.ID}}"
{{- range $_,$boardID := $.filterBoards -}}
{{if eq $boardID $board.ID}}checked{{end}}
{{- end}}></label>
{{- end -}} {{- end -}}
</td> </td>
</tr> </tr>
</table> </table>
<input type="submit" name="dofilter{{if gt $.filter.ID 0}}edit{{else}}add{{end}}" value="Submit Filter"> <input type="submit" name="dofilter{{if gt $.filter.ID 0}}edit{{else}}add{{end}}" value="Submit Filter">
{{- if gt $.filter.ID 0 -}}
<input type="button" onclick="window.location='{{webPath `manage/filters`}}'" value="Cancel"/>
{{- end -}}
</form> </form>
<hr/> <hr/>
<h2>Filter list</h2> <h2>Filter list</h2>