diff --git a/hasjob/models/jobpost.py b/hasjob/models/jobpost.py index 3a11c7679..68cfa4af2 100644 --- a/hasjob/models/jobpost.py +++ b/hasjob/models/jobpost.py @@ -199,6 +199,9 @@ def is_listed(self): return (self.status in POSTSTATUS.LISTED) and ( self.datetime > now - agelimit) + def is_public(self): + return self.status in POSTSTATUS.LISTED + def is_flagged(self): return self.status == POSTSTATUS.FLAGGED @@ -218,16 +221,38 @@ def pay_type_label(self): return PAY_TYPE.get(self.pay_type) def url_for(self, action='view', _external=False, **kwargs): + if self.status in POSTSTATUS.UNPUBLISHED and action in ('view', 'edit'): + domain = None + else: + domain = self.email_domain + if action == 'view': - return url_for('jobdetail', hashid=self.hashid, _external=_external, **kwargs) + return url_for('jobdetail', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'reveal': + return url_for('revealjob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'apply': + return url_for('applyjob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) elif action == 'edit': - return url_for('editjob', hashid=self.hashid, _external=_external, **kwargs) + return url_for('editjob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) elif action == 'withdraw': - return url_for('withdraw', hashid=self.hashid, _external=_external, **kwargs) + return url_for('withdraw', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'moderate': + return url_for('moderatejob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'pin': + return url_for('pinnedjob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'reject': + return url_for('rejectjob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) elif action == 'confirm': return url_for('confirm', hashid=self.hashid, _external=_external, **kwargs) elif action == 'logo': - return url_for('logoimage', hashid=self.hashid, _external=True, **kwargs) + return url_for('logoimage', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'confirm-link': + return url_for('confirm_email', hashid=self.hashid, domain=domain, + key=self.email_verify_key, _external=True, **kwargs) + elif action == 'star': + return url_for('starjob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) + elif action == 'manage': + return url_for('managejob', hashid=self.hashid, domain=domain, _external=_external, **kwargs) elif action == 'browse': if self.email_domain in webmail_domains: return url_for('browse_by_email', md5sum=self.md5sum, _external=_external, **kwargs) @@ -668,6 +693,18 @@ def application_count(self): 'rejected': counts[EMPLOYER_RESPONSE.REJECTED], } + def url_for(self, action='view', _external=False, **kwargs): + domain = self.jobpost.email_domain + if action == 'view': + return url_for('view_application', hashid=self.jobpost.hashid, domain=domain, application=self.hashid, + _external=_external, **kwargs) + elif action == 'process': + return url_for('process_application', hashid=self.jobpost.hashid, domain=domain, application=self.hashid, + _external=_external, **kwargs) + elif action == 'track-open': + return url_for('view_application_email_gif', hashid=self.jobpost.hashid, domain=domain, application=self.hashid, + _external=_external, **kwargs) + JobApplication.jobpost = db.relationship(JobPost, backref=db.backref('applications', order_by=( diff --git a/hasjob/templates/application.html b/hasjob/templates/application.html index 3d6532f2f..9c55f2b48 100644 --- a/hasjob/templates/application.html +++ b/hasjob/templates/application.html @@ -26,7 +26,7 @@

{{ job_application.fullname }}

{{ job_application.message|safe }}
-
+ {{ response_form.hidden_tag() }} @@ -97,7 +97,7 @@

{{ job_application.fullname }}

{% else %} {% endif %} {% endblock %} diff --git a/hasjob/templates/confirm_email.md b/hasjob/templates/confirm_email.md index 91c08d69e..6066d9953 100644 --- a/hasjob/templates/confirm_email.md +++ b/hasjob/templates/confirm_email.md @@ -6,7 +6,7 @@ This is a confirmation email for the job you listed at {% if g.board -%} {{ g.bo [Click here to confirm your email address and publish the job][confirm] -[confirm]: {{ url_for('confirm_email', _external=true, hashid=post.hashid, key=post.email_verify_key) }} +[confirm]: {{ post.url_for('confirm-link', _external=true) }} Save this email for the next 30 days while the post is active. Use these links if you need to edit the post, or if the position has been filled diff --git a/hasjob/templates/detail.html b/hasjob/templates/detail.html index a7e2f6e58..076ea631c 100644 --- a/hasjob/templates/detail.html +++ b/hasjob/templates/detail.html @@ -86,16 +86,16 @@

Job Perks

{% if not g.kiosk %}
- +    Email this - +    Share on WhatsApp - {%- if not g.kiosk and not post.is_announcement() %} @@ -124,9 +124,11 @@

What’s wrong with it?

   Edit this - -    Withdraw this - + {%- if post.is_public() %} + +    Withdraw this + + {%- endif %} {%- endif %} {%- if is_siteadmin %}
@@ -184,7 +186,7 @@

What’s wrong with it?

{%- endif %} {%- with appl=post.viewcounts['applied'], newappl=post.new_applications %}{% if appl %} - +    View candidates {%- if newappl %} {{ newappl }}/{{ appl }} {%- else %} {{ appl }} @@ -245,10 +247,10 @@

Apply for this position {% if jobview.applied %}{# User has already hit the reveal button #} {{ post.how_to_apply|hideemail }} {%- else %} - Show instructions + Show instructions {%- endif %} {%- else %} - Login with Twitter or Google + Login with Twitter or Google to see instructions on how to apply. Your identity will not be revealed to the employer. {%- endif %} diff --git a/hasjob/templates/inc/applyform.html b/hasjob/templates/inc/applyform.html index ba7e94596..8cdabe912 100644 --- a/hasjob/templates/inc/applyform.html +++ b/hasjob/templates/inc/applyform.html @@ -1,5 +1,5 @@ {% from "baseframe/forms.html" import renderfield, rendersubmit, ajaxform %} - + {{ applyform.hidden_tag() }} diff --git a/hasjob/templates/inc/moderateform.html b/hasjob/templates/inc/moderateform.html index 3c0b2165b..008de3b2d 100644 --- a/hasjob/templates/inc/moderateform.html +++ b/hasjob/templates/inc/moderateform.html @@ -1,5 +1,5 @@ {% from "baseframe/forms.html" import renderfield, ajaxform %} - + {{ moderateform.hidden_tag() }} diff --git a/hasjob/templates/inc/pinnedform.html b/hasjob/templates/inc/pinnedform.html index a7fdbb499..5df98c30c 100644 --- a/hasjob/templates/inc/pinnedform.html +++ b/hasjob/templates/inc/pinnedform.html @@ -1,5 +1,5 @@ {% from "baseframe/forms.html" import renderfield, ajaxform %} - + {{ pinnedform.hidden_tag() }} diff --git a/hasjob/templates/inc/rejectform.html b/hasjob/templates/inc/rejectform.html index ec7593fda..50ab44032 100644 --- a/hasjob/templates/inc/rejectform.html +++ b/hasjob/templates/inc/rejectform.html @@ -1,5 +1,5 @@ {% from "baseframe/forms.html" import renderfield, ajaxform %} - + {{ rejectform.hidden_tag() }} diff --git a/hasjob/templates/inc/replyform.html b/hasjob/templates/inc/replyform.html index e29228610..b42f1eccf 100644 --- a/hasjob/templates/inc/replyform.html +++ b/hasjob/templates/inc/replyform.html @@ -1,5 +1,5 @@ {% from "baseframe/forms.html" import renderfield %} - + {{ response_form.hidden_tag() }} diff --git a/hasjob/views/index.py b/hasjob/views/index.py index 7da864524..e1a6e9ce7 100644 --- a/hasjob/views/index.py +++ b/hasjob/views/index.py @@ -184,15 +184,21 @@ def browse_by_type(name): @csrf.exempt -@app.route('/at/', methods=['GET', 'POST'], subdomain='') -@app.route('/at/', methods=['GET', 'POST']) +@app.route('/', methods=['GET', 'POST'], subdomain='') +@app.route('/', methods=['GET', 'POST']) def browse_by_domain(domain): - if not domain: + if not domain or '.' not in domain: abort(404) basequery = JobPost.query.filter_by(email_domain=domain) return index(basequery=basequery, domain=domain, title=domain, showall=True) +@app.route('/at/', methods=['GET', 'POST'], subdomain='') +@app.route('/at/', methods=['GET', 'POST']) +def browse_by_domain_legacy(domain): + return redirect(url_for('browse_by_domain', domain=domain), code=301) + + @csrf.exempt @app.route('/category/', methods=['GET', 'POST'], subdomain='') @app.route('/category/', methods=['GET', 'POST']) diff --git a/hasjob/views/listing.py b/hasjob/views/listing.py index e2781af58..decb7d42a 100644 --- a/hasjob/views/listing.py +++ b/hasjob/views/listing.py @@ -43,9 +43,11 @@ from hasjob.views.helper import gif1x1 -@app.route('/view/', methods=('GET', 'POST'), subdomain='') -@app.route('/view/', methods=('GET', 'POST')) -def jobdetail(hashid): +@app.route('//', methods=('GET', 'POST'), subdomain='') +@app.route('//', methods=('GET', 'POST')) +@app.route('/view/', defaults={'domain': None}, methods=('GET', 'POST'), subdomain='') +@app.route('/view/', defaults={'domain': None}, methods=('GET', 'POST')) +def jobdetail(domain, hashid): post = JobPost.query.filter_by(hashid=hashid).first_or_404() # If we're on a board (that's now 'www') and this post isn't on this board, @@ -59,8 +61,12 @@ def jobdetail(hashid): else: return redirect(post.url_for(subdomain=None, _external=True)) + # If this post is past pending state and the domain doesn't match, redirect there + if post.status not in POSTSTATUS.UNPUBLISHED and post.email_domain != domain: + return redirect(post.url_for(), code=301) + if post.status in [POSTSTATUS.DRAFT, POSTSTATUS.PENDING]: - if not ((g.user and post.admin_is(g.user)) or post.edit_key in session.get('userkeys', [])): + if not ((g.user and post.admin_is(g.user))): abort(403) if post.status in POSTSTATUS.GONE: abort(410) @@ -165,10 +171,12 @@ def jobdetail(hashid): ) -@app.route('/star/', methods=['POST'], subdomain='') -@app.route('/star/', methods=['POST']) +@app.route('///star', defaults={'domain': None}, methods=['POST'], subdomain='') +@app.route('///star', defaults={'domain': None}, methods=['POST']) +@app.route('/star/', defaults={'domain': None}, methods=['POST'], subdomain='') +@app.route('/star/', defaults={'domain': None}, methods=['POST']) @lastuser.requires_login -def starjob(hashid): +def starjob(domain, hashid): """ Star/unstar a job """ @@ -187,14 +195,20 @@ def starjob(hashid): return response -@app.route('/reveal/', subdomain='') -@app.route('/reveal/') +@app.route('///reveal', subdomain='') +@app.route('///reveal') +@app.route('/reveal/', defaults={'domain': None}, subdomain='') +@app.route('/reveal/', defaults={'domain': None}) @lastuser.requires_login -def revealjob(hashid): +def revealjob(domain, hashid): """ This view is a GET request and that is intentional. """ post = JobPost.query.filter_by(hashid=hashid).first_or_404() + # If the domain doesn't match, redirect to correct URL + if post.email_domain != domain: + return redirect(post.url_for('reveal'), code=301) + if post.status in [POSTSTATUS.REJECTED, POSTSTATUS.WITHDRAWN, POSTSTATUS.SPAM]: abort(410) jobview = UserJobView.query.get((post.id, g.user.id)) @@ -225,13 +239,19 @@ def revealjob(hashid): return redirect(post.url_for(), 303) -@app.route('/apply/', methods=['POST'], subdomain='') -@app.route('/apply/', methods=['POST']) -def applyjob(hashid): +@app.route('///apply', methods=['POST'], subdomain='') +@app.route('///apply', methods=['POST']) +@app.route('/apply/', defaults={'domain': None}, methods=['POST'], subdomain='') +@app.route('/apply/', defaults={'domain': None}, methods=['POST']) +def applyjob(domain, hashid): """ Apply to a job (including in kiosk mode) """ post = JobPost.query.filter_by(hashid=hashid).first_or_404() + # If the domain doesn't match, redirect to correct URL + if post.email_domain != domain: + return redirect(post.url_for('apply'), code=301) + if g.user: job_application = JobApplication.query.filter_by(user=g.user, jobpost=post).first() else: @@ -274,9 +294,7 @@ def applyjob(hashid): email_html = email_transform( render_template('apply_email.html', post=post, job_application=job_application, - archive_url=url_for('view_application', - hashid=post.hashid, application=job_application.hashid, - _external=True)), + archive_url=job_application.url_for(_external=True)), base_url=request.url_root) email_text = html2text(email_html) flashmsg = "Your application has been sent to the employer" @@ -304,19 +322,28 @@ def applyjob(hashid): return redirect(post.url_for(), 303) -@app.route('/manage/', methods=('GET', 'POST'), defaults={'key': None}, subdomain='') -@app.route('/manage/', methods=('GET', 'POST'), defaults={'key': None}) -@load_model(JobPost, {'hashid': 'hashid'}, 'post', permission=('manage', 'siteadmin'), addlperms=lastuser.permissions) -def managejob(post): +@app.route('///manage', methods=('GET', 'POST'), defaults={'key': None}, subdomain='') +@app.route('///manage', methods=('GET', 'POST'), defaults={'key': None}) +@app.route('/manage/', methods=('GET', 'POST'), defaults={'key': None, 'domain': None}, subdomain='') +@app.route('/manage/', methods=('GET', 'POST'), defaults={'key': None, 'domain': None}) +@load_model(JobPost, {'hashid': 'hashid'}, 'post', permission=('manage', 'siteadmin'), addlperms=lastuser.permissions, + kwargs=True) +def managejob(post, kwargs): + # If the domain doesn't match, redirect to correct URL + if post.email_domain != kwargs.get('domain'): + return redirect(post.url_for('manage'), code=301) + if post.applications: - return redirect(url_for('view_application', hashid=post.hashid, application=post.applications[0].hashid)) + return redirect(post.applications[0].url_for(), code=303) else: return redirect(post.url_for()) -@app.route('/view///track.gif', subdomain='') -@app.route('/view///track.gif') -def view_application_email_gif(hashid, application): +@app.route('///appl//track.gif', subdomain='') +@app.route('///appl//track.gif') +@app.route('/view///track.gif', defaults={'domain': None}, subdomain='') +@app.route('/view///track.gif', defaults={'domain': None}) +def view_application_email_gif(domain, hashid, application): post = JobPost.query.filter_by(hashid=hashid).one_or_none() if post: # FIXME: Can't use one_or_none() until we ensure jobpost_id+user_id is unique @@ -343,9 +370,11 @@ def view_application_email_gif(hashid, application): } -@app.route('/view//', subdomain='') -@app.route('/view//') -def view_application(hashid, application): +@app.route('///appl/', subdomain='') +@app.route('///appl/') +@app.route('/view//', defaults={'domain': None}, subdomain='') +@app.route('/view//', defaults={'domain': None}) +def view_application(domain, hashid, application): post = JobPost.query.filter_by(hashid=hashid).first_or_404() # Transition code until we force all employers to login before posting if post.user and not (post.admin_is(g.user) or lastuser.has_permission('siteadmin')): @@ -354,6 +383,11 @@ def view_application(hashid, application): else: abort(403) job_application = JobApplication.query.filter_by(hashid=application, jobpost=post).first_or_404() + + # If this domain doesn't match, redirect to correct URL + if post.email_domain != domain: + return redirect(job_application.url_for(), code=301) + if job_application.response == EMPLOYER_RESPONSE.NEW: # If the application is pending, mark it as opened. # However, don't do this if the user is a siteadmin, unless they also own the post. @@ -378,9 +412,11 @@ def view_application(hashid, application): response_form=response_form, statuses=statuses, is_siteadmin=lastuser.has_permission('siteadmin')) -@app.route('/apply//', methods=['POST'], subdomain='') -@app.route('/apply//', methods=['POST']) -def process_application(hashid, application): +@app.route('///appl//process', methods=['POST'], subdomain='') +@app.route('///appl//process', methods=['POST']) +@app.route('/apply//', defaults={'domain': None}, methods=['POST'], subdomain='') +@app.route('/apply//', defaults={'domain': None}, methods=['POST']) +def process_application(domain, hashid, application): post = JobPost.query.filter_by(hashid=hashid).first_or_404() if post.user and not post.admin_is(g.user): if not g.user: @@ -408,9 +444,7 @@ def process_application(hashid, application): email_html = email_transform( render_template('respond_email.html', post=post, job_application=job_application, - archive_url=url_for('view_application', - hashid=post.hashid, application=job_application.hashid, - _external=True)), + archive_url=job_application.url_for(_external=True)), base_url=request.url_root) email_text = html2text(email_html) @@ -451,13 +485,15 @@ def process_application(hashid, application): else: flash(flashmsg, 'interactive') - return redirect(url_for('view_application', hashid=post.hashid, application=job_application.hashid), 303) + return redirect(job_application.url_for(), 303) -@app.route('/pinned/', methods=['POST'], subdomain='') -@app.route('/pinned/', methods=['POST']) +@app.route('///pin', methods=['POST'], subdomain='') +@app.route('///pin', methods=['POST']) +@app.route('/pinned/', defaults={'domain': None}, methods=['POST'], subdomain='') +@app.route('/pinned/', defaults={'domain': None}, methods=['POST']) @lastuser.requires_permission('siteadmin') -def pinnedjob(hashid): +def pinnedjob(domain, hashid): post = JobPost.query.filter_by(hashid=hashid).first_or_404() if g.board: obj = post.link_to_board(g.board) @@ -482,14 +518,15 @@ def pinnedjob(hashid): return redirect(post.url_for(), 303) -@app.route('/reject/', methods=('GET', 'POST'), subdomain='') -@app.route('/reject/', methods=('GET', 'POST')) +@app.route('///reject', methods=('GET', 'POST'), subdomain='') +@app.route('///reject', methods=('GET', 'POST')) +@app.route('/reject/', defaults={'domain': None}, methods=('GET', 'POST'), subdomain='') +@app.route('/reject/', defaults={'domain': None}, methods=('GET', 'POST')) @lastuser.requires_permission('siteadmin') -def rejectjob(hashid): +def rejectjob(domain, hashid): post = JobPost.query.filter_by(hashid=hashid).first_or_404() - if post.status in [POSTSTATUS.DRAFT, POSTSTATUS.PENDING]: - if post.edit_key not in session.get('userkeys', []): - abort(403) + if post.status in [POSTSTATUS.DRAFT, POSTSTATUS.PENDING] and not post.admin_is(g.user): + abort(403) if post.status in [POSTSTATUS.REJECTED, POSTSTATUS.WITHDRAWN, POSTSTATUS.SPAM]: abort(410) rejectform = forms.RejectForm() @@ -520,14 +557,15 @@ def rejectjob(hashid): return redirect(post.url_for(), code=303) -@app.route('/moderate/', methods=('GET', 'POST'), subdomain='') -@app.route('/moderate/', methods=('GET', 'POST')) +@app.route('///moderate', methods=('GET', 'POST'), subdomain='') +@app.route('///moderate', methods=('GET', 'POST')) +@app.route('/moderate/', defaults={'domain': None}, methods=('GET', 'POST'), subdomain='') +@app.route('/moderate/', defaults={'domain': None}, methods=('GET', 'POST')) @lastuser.requires_permission('siteadmin') -def moderatejob(hashid): +def moderatejob(domain, hashid): post = JobPost.query.filter_by(hashid=hashid).first_or_404() if post.status in [POSTSTATUS.DRAFT, POSTSTATUS.PENDING]: - if post.edit_key not in session.get('userkeys', []): - abort(403) + abort(403) if post.status in [POSTSTATUS.REJECTED, POSTSTATUS.WITHDRAWN, POSTSTATUS.SPAM]: abort(410) moderateform = forms.ModerateForm() @@ -552,15 +590,16 @@ def moderatejob(hashid): @csrf.exempt -@app.route('/confirm/', methods=('GET', 'POST'), subdomain='') -@app.route('/confirm/', methods=('GET', 'POST')) -def confirm(hashid): +@app.route('///confirm', methods=('GET', 'POST'), subdomain='') +@app.route('///confirm', methods=('GET', 'POST')) +@app.route('/confirm/', defaults={'domain': None}, methods=('GET', 'POST'), subdomain='') +@app.route('/confirm/', defaults={'domain': None}, methods=('GET', 'POST')) +def confirm(domain, hashid): post = JobPost.query.filter_by(hashid=hashid).first_or_404() form = forms.ConfirmForm() if post.status in POSTSTATUS.GONE: abort(410) - elif post.status in POSTSTATUS.UNPUBLISHED: - if not (post.edit_key in session.get('userkeys', []) or post.admin_is(g.user)): + elif post.status in POSTSTATUS.UNPUBLISHED and not post.admin_is(g.user): abort(403) else: # Any other status: no confirmation required (via this handler) @@ -581,18 +620,15 @@ def confirm(hashid): post.status = POSTSTATUS.PENDING db.session.commit() - try: - session.get('userkeys', []).remove(post.edit_key) - session.modified = True # Since it won't detect changes to lists - except ValueError: - pass return render_template('mailsent.html', post=post) return render_template('confirm.html', post=post, form=form) -@app.route('/confirm//', subdomain='') -@app.route('/confirm//') -def confirm_email(hashid, key): +@app.route('///confirm/', subdomain='') +@app.route('///confirm/') +@app.route('/confirm//', defaults={'domain': None}, subdomain='') +@app.route('/confirm//', defaults={'domain': None}) +def confirm_email(domain, hashid, key): # If post is in pending state and email key is correct, convert to published # and update post.datetime to utcnow() so it'll show on top of the stack # This function expects key to be email_verify_key, not edit_key like the others @@ -605,7 +641,7 @@ def confirm_email(hashid, key): elif post.status == POSTSTATUS.DRAFT: # This should not happen. The user doesn't have this URL until they # pass the confirm form - return redirect(url_for('confirm', hashid=post.hashid), code=302) + return redirect(post.url_for('confirm'), code=302) elif post.status == POSTSTATUS.PENDING: if key != post.email_verify_key: abort(403) @@ -634,11 +670,13 @@ def confirm_email(hashid, key): return redirect(post.url_for(), code=302) -@app.route('/withdraw/', methods=('GET', 'POST'), defaults={'key': None}, subdomain='') -@app.route('/withdraw//', methods=('GET', 'POST'), subdomain='') -@app.route('/withdraw/', methods=('GET', 'POST'), defaults={'key': None}) -@app.route('/withdraw//', methods=('GET', 'POST')) -def withdraw(hashid, key): +@app.route('///withdraw', methods=('GET', 'POST'), defaults={'key': None}, subdomain='') +@app.route('///withdraw', methods=('GET', 'POST'), defaults={'key': None}) +@app.route('/withdraw/', methods=('GET', 'POST'), defaults={'key': None, 'domain': None}, subdomain='') +@app.route('/withdraw//', defaults={'domain': None}, methods=('GET', 'POST'), subdomain='') +@app.route('/withdraw/', methods=('GET', 'POST'), defaults={'key': None, 'domain': None}) +@app.route('/withdraw//', defaults={'domain': None}, methods=('GET', 'POST')) +def withdraw(domain, hashid, key): post = JobPost.query.filter_by(hashid=hashid).first_or_404() form = forms.WithdrawForm() if not ((key is None and g.user is not None and post.admin_is(g.user)) or (key == post.edit_key)): @@ -658,11 +696,13 @@ def withdraw(hashid, key): return render_template("withdraw.html", post=post, form=form) -@app.route('/edit/', methods=('GET', 'POST'), defaults={'key': None}, subdomain='') -@app.route('/edit//', methods=('GET', 'POST'), subdomain='') -@app.route('/edit/', methods=('GET', 'POST'), defaults={'key': None}) -@app.route('/edit//', methods=('GET', 'POST')) -def editjob(hashid, key, form=None, validated=False, newpost=None): +@app.route('///edit', methods=('GET', 'POST'), defaults={'key': None}, subdomain='') +@app.route('///edit', methods=('GET', 'POST'), defaults={'key': None}) +@app.route('/edit/', methods=('GET', 'POST'), defaults={'key': None, 'domain': None}, subdomain='') +@app.route('/edit//', defaults={'domain': None}, methods=('GET', 'POST'), subdomain='') +@app.route('/edit/', methods=('GET', 'POST'), defaults={'key': None, 'domain': None}) +@app.route('/edit//', defaults={'domain': None}, methods=('GET', 'POST')) +def editjob(hashid, key, domain=None, form=None, validated=False, newpost=None): if form is None: form = forms.ListingForm(request.form) form.job_type.choices = JobType.choices(g.board) @@ -678,6 +718,10 @@ def editjob(hashid, key, form=None, validated=False, newpost=None): if not ((key is None and g.user is not None and post.admin_is(g.user)) or (key == post.edit_key)): abort(403) + # Once this post is published, require editing at /domain//edit + if not key and post.status not in POSTSTATUS.UNPUBLISHED and post.email_domain != domain: + return redirect(post.url_for('edit'), code=301) + # Don't allow editing jobs that aren't on this board as that may be a loophole when # the board allows no pay (except in the 'www' root board, where editing is always allowed) with db.session.no_autoflush: @@ -794,9 +838,7 @@ def editjob(hashid, key, form=None, validated=False, newpost=None): tag_jobpost.delay(post.id) # Keywords tag_locations.delay(post.id) # Locations post.uncache_viewcounts('pay_label') - userkeys = session.get('userkeys', []) - userkeys.append(post.edit_key) - session['userkeys'] = userkeys + session.pop('userkeys', None) # Remove legacy userkeys dict session.permanent = True return redirect(post.url_for(), code=303) elif request.method == 'POST':