mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-28 16:28:01 -05:00
Remove obsolete house ad system.
This has been replaced by reddit-plugin-adzerk and Adzerk.net.
This commit is contained in:
@@ -110,11 +110,6 @@ def make_map():
|
||||
mc('/feedback', controller='feedback', action='feedback')
|
||||
mc('/ad_inq', controller='feedback', action='ad_inq')
|
||||
|
||||
# Used for editing ads
|
||||
mc('/admin/ads', controller='ads')
|
||||
mc('/admin/ads/:adcn/:action', controller='ads',
|
||||
requirements=dict(action="assign|srs"))
|
||||
|
||||
mc('/admin/awards', controller='awards')
|
||||
mc('/admin/awards/:awardcn/:action', controller='awards',
|
||||
requirements=dict(action="give|winners"))
|
||||
@@ -334,9 +329,6 @@ def make_map():
|
||||
|
||||
# Used for showing ads
|
||||
mc("/ads/", controller="ad", action="ad")
|
||||
mc("/ads/r/:reddit_name/:keyword", controller="ad", action="ad",
|
||||
keyword=None)
|
||||
mc("/ads/:codename", controller="ad", action="ad_by_codename")
|
||||
|
||||
mc("/try", controller="forms", action="try_compact")
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ def load_controllers():
|
||||
from post import PostController
|
||||
from toolbar import ToolbarController
|
||||
from awards import AwardsController
|
||||
from ads import AdsController
|
||||
from errorlog import ErrorlogController
|
||||
from promotecontroller import PromoteController
|
||||
from mediaembed import MediaembedController
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of
|
||||
# the Original Code is reddit Inc.
|
||||
#
|
||||
# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit
|
||||
# Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
from pylons import request, g
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.pages import AdminPage, AdminAds, AdminAdAssign, AdminAdSRs
|
||||
from r2.lib.validator import *
|
||||
|
||||
class AdsController(RedditController):
|
||||
|
||||
@validate(VSponsorAdmin())
|
||||
def GET_index(self):
|
||||
res = AdminPage(content = AdminAds(),
|
||||
show_sidebar = False,
|
||||
title = 'ads').render()
|
||||
return res
|
||||
|
||||
@validate(VSponsorAdmin(),
|
||||
ad = VAdByCodename('adcn'))
|
||||
def GET_assign(self, ad):
|
||||
if ad is None:
|
||||
abort(404, 'page not found')
|
||||
|
||||
res = AdminPage(content = AdminAdAssign(ad),
|
||||
show_sidebar = False,
|
||||
title='assign an ad to a community').render()
|
||||
return res
|
||||
|
||||
@validate(VSponsorAdmin(),
|
||||
ad = VAdByCodename('adcn'))
|
||||
def GET_srs(self, ad):
|
||||
if ad is None:
|
||||
abort(404, 'page not found')
|
||||
|
||||
res = AdminPage(content = AdminAdSRs(ad),
|
||||
show_sidebar = False,
|
||||
title='ad srs').render()
|
||||
return res
|
||||
@@ -2492,91 +2492,6 @@ class ApiController(RedditController, OAuth2ResourceController):
|
||||
|
||||
form.set_html(".status", _('saved'))
|
||||
|
||||
@validatedForm(VSponsor(),
|
||||
ad = VByName("fullname"),
|
||||
colliding_ad=VAdByCodename(("codename", "fullname")),
|
||||
codename = VLength("codename", max_length = 100),
|
||||
imgurl = VLength("imgurl", max_length = 1000),
|
||||
raw_html = VLength("raw_html", max_length = 10000),
|
||||
linkurl = VLength("linkurl", max_length = 1000))
|
||||
def POST_editad(self, form, jquery, ad, colliding_ad, codename,
|
||||
imgurl, raw_html, linkurl):
|
||||
if form.has_errors(("codename", "imgurl", "linkurl"),
|
||||
errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors(("codename"), errors.INVALID_OPTION):
|
||||
form.set_html(".status", "some other ad has that codename")
|
||||
pass
|
||||
|
||||
if form.has_error():
|
||||
return
|
||||
|
||||
if ad is None:
|
||||
Ad._new(codename,
|
||||
imgurl=imgurl,
|
||||
raw_html=raw_html,
|
||||
linkurl=linkurl)
|
||||
form.set_html(".status", "saved. reload to see it.")
|
||||
return
|
||||
|
||||
ad.codename = codename
|
||||
ad.imgurl = imgurl
|
||||
ad.raw_html = raw_html
|
||||
ad.linkurl = linkurl
|
||||
ad._commit()
|
||||
form.set_html(".status", _('saved'))
|
||||
|
||||
@validatedForm(VSponsor(),
|
||||
ad = VByName("fullname"),
|
||||
sr = VSubmitSR("community"),
|
||||
weight = VInt("weight",
|
||||
coerce=False, min=0, max=100000),
|
||||
)
|
||||
def POST_assignad(self, form, jquery, ad, sr, weight):
|
||||
if form.has_errors("ad", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors("community", errors.SUBREDDIT_REQUIRED,
|
||||
errors.SUBREDDIT_NOEXIST, errors.SUBREDDIT_NOTALLOWED):
|
||||
pass
|
||||
|
||||
if form.has_errors("fullname", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors("weight", errors.BAD_NUMBER):
|
||||
pass
|
||||
|
||||
if form.has_error():
|
||||
return
|
||||
|
||||
if ad.codename == "DART" and sr.name == g.default_sr and weight != 100:
|
||||
log_text("Bad default DART weight",
|
||||
"The default DART weight can only be 100, not %s."
|
||||
% weight,
|
||||
"error")
|
||||
abort(403, 'forbidden')
|
||||
|
||||
existing = AdSR.by_ad_and_sr(ad, sr)
|
||||
|
||||
if weight is not None:
|
||||
if existing:
|
||||
existing.weight = weight
|
||||
existing._commit()
|
||||
else:
|
||||
AdSR._new(ad, sr, weight)
|
||||
|
||||
form.set_html(".status", _('saved'))
|
||||
|
||||
else:
|
||||
if existing:
|
||||
existing._delete()
|
||||
AdSR.by_ad(ad, _update=True)
|
||||
AdSR.by_sr(sr, _update=True)
|
||||
|
||||
form.set_html(".status", _('deleted'))
|
||||
|
||||
|
||||
@validatedForm(VAdmin(),
|
||||
award=VByName("fullname"),
|
||||
colliding_award=VAwardByCodename(("codename", "fullname")),
|
||||
|
||||
@@ -658,8 +658,6 @@ class FrontController(RedditController, OAuth2ResourceController):
|
||||
elif is_moderator_with_perms('flair') and location == 'flair':
|
||||
c.allow_styles = True
|
||||
pane = FlairPane(num, after, reverse, name, user)
|
||||
elif c.user_is_sponsor and location == 'ads':
|
||||
pane = RedditAds()
|
||||
elif (location == "about") and is_api():
|
||||
return self.redirect(add_sr('about.json'), code=301)
|
||||
else:
|
||||
|
||||
@@ -23,14 +23,12 @@
|
||||
from reddit_base import MinimalController
|
||||
|
||||
from r2.lib.scraper import get_media_embed
|
||||
from r2.lib.pages import MediaEmbedBody, render_ad, render_ad_by_codename
|
||||
from r2.lib.pages import MediaEmbedBody
|
||||
from r2.lib.validator import *
|
||||
|
||||
from pylons import request
|
||||
from pylons.controllers.util import abort
|
||||
from r2.lib.cache import make_key
|
||||
|
||||
import random
|
||||
|
||||
class MediaembedController(MinimalController):
|
||||
@validate(link = VLink('link'))
|
||||
@@ -54,23 +52,10 @@ class MediaembedController(MinimalController):
|
||||
|
||||
return MediaEmbedBody(body = content).render()
|
||||
|
||||
|
||||
class AdController(MinimalController):
|
||||
def request_key(self):
|
||||
return make_key('request',
|
||||
c.lang,
|
||||
c.content_langs,
|
||||
request.host,
|
||||
c.cname,
|
||||
request.fullpath,
|
||||
random.choice(xrange(100)))
|
||||
|
||||
def GET_ad(self, reddit_name = None, keyword=None):
|
||||
c.render_style = "html"
|
||||
return render_ad(reddit_name=reddit_name, keyword=keyword)
|
||||
|
||||
def GET_ad_by_codename(self, codename = None):
|
||||
if not codename:
|
||||
abort(404)
|
||||
c.render_style = "html"
|
||||
return render_ad_by_codename(codename)
|
||||
def try_pagecache(self):
|
||||
pass
|
||||
|
||||
def GET_ad(self):
|
||||
return "This is a placeholder ad."
|
||||
|
||||
@@ -34,7 +34,6 @@ from r2.lib.menus import (
|
||||
def admin_menu(**kwargs):
|
||||
buttons = [
|
||||
OffsiteButton("traffic", "/traffic"),
|
||||
NavButton(menu.ads, "ads"),
|
||||
NavButton(menu.awards, "awards"),
|
||||
NavButton(menu.errors, "error log"),
|
||||
]
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
from r2.lib.wrapped import Wrapped, Templated, CachedTemplate
|
||||
from r2.models import Account, FakeAccount, DefaultSR, make_feedurl
|
||||
from r2.models import FakeSubreddit, Subreddit, Ad, AdSR, SubSR, AllMinus, AllSR
|
||||
from r2.models import FakeSubreddit, Subreddit, SubSR, AllMinus, AllSR
|
||||
from r2.models import Friends, All, Sub, NotFound, DomainSR, Random, Mod, RandomNSFW, RandomSubscription, MultiReddit, ModSR, Frontpage
|
||||
from r2.models import Link, Printable, Trophy, bidding, PromoCampaign, PromotionWeights, Comment
|
||||
from r2.models import Flair, FlairTemplate, FlairTemplateBySubredditIndex
|
||||
@@ -2400,41 +2400,6 @@ class AdminErrorLog(Templated):
|
||||
|
||||
Templated.__init__(self)
|
||||
|
||||
class AdminAds(Templated):
|
||||
"""The admin page for editing ads"""
|
||||
def __init__(self):
|
||||
from r2.models import Ad
|
||||
Templated.__init__(self)
|
||||
self.ads = Ad._all_ads()
|
||||
|
||||
class AdminAdAssign(Templated):
|
||||
"""The interface for assigning an ad to a community"""
|
||||
def __init__(self, ad):
|
||||
self.weight = 100
|
||||
Templated.__init__(self, ad = ad)
|
||||
|
||||
class AdminAdSRs(Templated):
|
||||
"""View the communities an ad is running on"""
|
||||
def __init__(self, ad):
|
||||
self.adsrs = AdSR.by_ad(ad)
|
||||
|
||||
# Create a dictionary of
|
||||
# SR => total weight of all its ads
|
||||
# for all SRs that this ad is running on
|
||||
self.sr_totals = {}
|
||||
for adsr in self.adsrs:
|
||||
sr = adsr._thing2
|
||||
|
||||
if sr.name not in self.sr_totals:
|
||||
# We haven't added up this SR yet.
|
||||
self.sr_totals[sr.name] = 0
|
||||
# Get all its ads and total them up.
|
||||
sr_adsrs = AdSR.by_sr_merged(sr)
|
||||
for adsr2 in sr_adsrs:
|
||||
self.sr_totals[sr.name] += adsr2.weight
|
||||
|
||||
Templated.__init__(self, ad = ad)
|
||||
|
||||
class AdminAwards(Templated):
|
||||
"""The admin page for editing awards"""
|
||||
def __init__(self):
|
||||
@@ -2464,15 +2429,11 @@ class AdminAwardWinners(Templated):
|
||||
trophies = Trophy.by_award(award)
|
||||
Templated.__init__(self, award = award, trophies = trophies)
|
||||
|
||||
|
||||
class Ads(Templated):
|
||||
def __init__(self):
|
||||
Templated.__init__(self)
|
||||
path = ""
|
||||
if c.custom_dart_keyword:
|
||||
path = "r/%s/%s" % (c.site.name, c.custom_dart_keyword)
|
||||
elif not c.default_sr:
|
||||
path = "r/%s/" % c.site.name
|
||||
self.ad_url = g.ad_domain + "/ads/" + path
|
||||
self.ad_url = g.ad_domain + "/ads/"
|
||||
self.frame_id = "ad-frame"
|
||||
|
||||
|
||||
@@ -3487,27 +3448,6 @@ class MediaEmbedBody(CachedTemplate):
|
||||
res = CachedTemplate.render(self, *a, **kw)
|
||||
return responsive(res, True)
|
||||
|
||||
class RedditAds(Templated):
|
||||
def __init__(self, **kw):
|
||||
self.sr_name = c.site.name
|
||||
self.adsrs = AdSR.by_sr_merged(c.site)
|
||||
self.total = 0
|
||||
|
||||
self.adsrs.sort(key=lambda a: a._thing1.codename)
|
||||
|
||||
seen = {}
|
||||
for adsr in self.adsrs:
|
||||
seen[adsr._thing1.codename] = True
|
||||
self.total += adsr.weight
|
||||
|
||||
self.other_ads = []
|
||||
all_ads = Ad._all_ads()
|
||||
all_ads.sort(key=lambda a: a.codename)
|
||||
for ad in all_ads:
|
||||
if ad.codename not in seen:
|
||||
self.other_ads.append(ad)
|
||||
|
||||
Templated.__init__(self, **kw)
|
||||
|
||||
class PaymentForm(Templated):
|
||||
def __init__(self, link, campaign, **kw):
|
||||
@@ -3786,80 +3726,6 @@ class RawString(Templated):
|
||||
def render(self, *a, **kw):
|
||||
return unsafe(self.s)
|
||||
|
||||
class Dart_Ad(CachedTemplate):
|
||||
def __init__(self, dartsite, tag, custom_keyword=None):
|
||||
tag = tag or "homepage"
|
||||
keyword = custom_keyword or tag
|
||||
tracker_url = tracking.get_impression_pixel_url("dart_" + tag)
|
||||
Templated.__init__(self, tag = tag, dartsite = dartsite,
|
||||
tracker_url = tracker_url, keyword=keyword)
|
||||
|
||||
def render(self, *a, **kw):
|
||||
res = CachedTemplate.render(self, *a, **kw)
|
||||
return responsive(res, False)
|
||||
|
||||
class HouseAd(CachedTemplate):
|
||||
def __init__(self, rendering, linkurl, submit_link):
|
||||
Templated.__init__(self, rendering=rendering,
|
||||
linkurl = linkurl,
|
||||
submit_link = submit_link)
|
||||
|
||||
def render(self, *a, **kw):
|
||||
res = CachedTemplate.render(self, *a, **kw)
|
||||
return responsive(res, False)
|
||||
|
||||
|
||||
def render_ad_by_codename(codename):
|
||||
if codename == "DART":
|
||||
return Dart_Ad("reddit.dart", g.default_sr).render()
|
||||
|
||||
try:
|
||||
ad = Ad._by_codename(codename)
|
||||
except NotFound:
|
||||
abort(404)
|
||||
attrs = ad.important_attrs()
|
||||
return HouseAd(**attrs).render()
|
||||
|
||||
|
||||
def render_ad(reddit_name=None, keyword=None):
|
||||
if not reddit_name:
|
||||
reddit_name = g.default_sr
|
||||
if g.live_config["frontpage_dart"]:
|
||||
return Dart_Ad("reddit.dart", reddit_name, keyword).render()
|
||||
|
||||
try:
|
||||
sr = Subreddit._by_name(reddit_name, stale=True)
|
||||
except NotFound:
|
||||
return Dart_Ad("reddit.dart", g.default_sr, keyword).render()
|
||||
|
||||
if sr.over_18:
|
||||
dartsite = "reddit.dart.nsfw"
|
||||
else:
|
||||
dartsite = "reddit.dart"
|
||||
|
||||
if keyword:
|
||||
return Dart_Ad(dartsite, reddit_name, keyword).render()
|
||||
|
||||
ads = {}
|
||||
|
||||
for adsr in AdSR.by_sr_merged(sr):
|
||||
ad = adsr._thing1
|
||||
ads[ad.codename] = (ad, adsr.weight)
|
||||
|
||||
try:
|
||||
codename = weighted_lottery({k: v[1] for k, v in ads.iteritems()})
|
||||
except ValueError, ex:
|
||||
log_text(
|
||||
"no winner",
|
||||
"No winner found for /r/%s, error=%s" % (reddit_name, ex.message),
|
||||
"error")
|
||||
codename = "DART"
|
||||
|
||||
if codename == "DART":
|
||||
return Dart_Ad(dartsite, reddit_name).render()
|
||||
else:
|
||||
attrs = ads[codename][0].important_attrs()
|
||||
return HouseAd(**attrs).render()
|
||||
|
||||
class TryCompact(Reddit):
|
||||
def __init__(self, dest, **kw):
|
||||
|
||||
@@ -402,24 +402,6 @@ class VCommentByID(VThing):
|
||||
def __init__(self, param, redirect = True, *a, **kw):
|
||||
VThing.__init__(self, param, Comment, redirect=redirect, *a, **kw)
|
||||
|
||||
class VAd(VThing):
|
||||
def __init__(self, param, redirect = True, *a, **kw):
|
||||
VThing.__init__(self, param, Ad, redirect=redirect, *a, **kw)
|
||||
|
||||
class VAdByCodename(Validator):
|
||||
def run(self, codename, required_fullname=None):
|
||||
if not codename:
|
||||
return self.set_error(errors.NO_TEXT)
|
||||
|
||||
try:
|
||||
a = Ad._by_codename(codename)
|
||||
except NotFound:
|
||||
a = None
|
||||
|
||||
if a and required_fullname and a._fullname != required_fullname:
|
||||
return self.set_error(errors.INVALID_OPTION)
|
||||
else:
|
||||
return a
|
||||
|
||||
class VAward(VThing):
|
||||
def __init__(self, param, redirect = True, *a, **kw):
|
||||
|
||||
@@ -29,7 +29,6 @@ from report import *
|
||||
from subreddit import *
|
||||
from flair import *
|
||||
from award import *
|
||||
from ad import *
|
||||
from bidding import *
|
||||
from mail_queue import Email, has_opted_out, opt_count
|
||||
from gold import *
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of
|
||||
# the Original Code is reddit Inc.
|
||||
#
|
||||
# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit
|
||||
# Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
from r2.lib.db.thing import Thing, Relation, NotFound
|
||||
from r2.lib.db.operators import asc, desc, lower
|
||||
from r2.lib.filters import _force_unicode
|
||||
from r2.lib.memoize import memoize
|
||||
from r2.models import Subreddit
|
||||
from pylons import c, g, request
|
||||
|
||||
class Ad (Thing):
|
||||
_defaults = dict(
|
||||
codename = None,
|
||||
imgurl = None,
|
||||
linkurl = None,
|
||||
raw_html = None,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@memoize('ad.all_ads')
|
||||
def _all_ads_cache(cls):
|
||||
return [ a._id for a in Ad._query(sort=desc('_date'), limit=1000) ]
|
||||
|
||||
@classmethod
|
||||
def _all_ads(cls, _update=False):
|
||||
all = cls._all_ads_cache(_update=_update)
|
||||
# Can't just return Ad._byID() results because
|
||||
# the ordering will be lost
|
||||
d = Ad._byID(all, data=True)
|
||||
return [ d[id] for id in all ]
|
||||
|
||||
@classmethod
|
||||
def _new(cls, codename, imgurl, raw_html, linkurl):
|
||||
a = Ad(codename=codename, imgurl=imgurl, raw_html=raw_html,
|
||||
linkurl=linkurl)
|
||||
a._commit()
|
||||
Ad._all_ads_cache(_update=True)
|
||||
|
||||
@classmethod
|
||||
def _by_codename(cls, codename):
|
||||
q = cls._query(lower(Ad.c.codename) == codename.lower())
|
||||
q._limit = 1
|
||||
ad = list(q)
|
||||
|
||||
if ad:
|
||||
return cls._byID(ad[0]._id, True)
|
||||
else:
|
||||
raise NotFound, 'Ad %s' % codename
|
||||
|
||||
def url(self):
|
||||
return "%s/ads/%s" % (g.ad_domain, self.codename)
|
||||
|
||||
def submit_link(self):
|
||||
from r2.lib.template_helpers import get_domain
|
||||
from mako.filters import url_escape
|
||||
|
||||
d = get_domain(subreddit=False)
|
||||
u = _force_unicode(self.url())
|
||||
|
||||
return "http://%s/r/ads/submit?url=%s" % (d, url_escape(u))
|
||||
|
||||
def rendering(self):
|
||||
if self.raw_html:
|
||||
return self.raw_html
|
||||
else:
|
||||
return "<img src='%s' />" % self.imgurl
|
||||
|
||||
def important_attrs(self):
|
||||
return dict(rendering=self.rendering(), linkurl=self.linkurl,
|
||||
submit_link=self.submit_link())
|
||||
|
||||
class AdSR(Relation(Ad, Subreddit)):
|
||||
@classmethod
|
||||
def _new(cls, ad, sr, weight=100):
|
||||
t = AdSR(ad, sr, "adsr")
|
||||
t.weight = weight
|
||||
t._commit()
|
||||
|
||||
AdSR.by_ad(ad, _update=True)
|
||||
AdSR.by_sr(sr, _update=True)
|
||||
|
||||
@classmethod
|
||||
@memoize('adsr.by_ad')
|
||||
def by_ad_cache(cls, ad_id):
|
||||
q = AdSR._query(AdSR.c._thing1_id == ad_id,
|
||||
sort = desc('_date'))
|
||||
q._limit = 500
|
||||
return [ t._id for t in q ]
|
||||
|
||||
@classmethod
|
||||
def by_ad(cls, ad, _update=False):
|
||||
rel_ids = cls.by_ad_cache(ad._id, _update=_update)
|
||||
adsrs = AdSR._byID_rel(rel_ids, data=True, eager_load=True,
|
||||
thing_data=True, return_dict = False)
|
||||
return adsrs
|
||||
|
||||
@classmethod
|
||||
@memoize('adsr.by_sr')
|
||||
def by_sr_cache(cls, sr_id):
|
||||
q = AdSR._query(AdSR.c._thing2_id == sr_id,
|
||||
sort = desc('_date'))
|
||||
q._limit = 500
|
||||
return [ t._id for t in q ]
|
||||
|
||||
@classmethod
|
||||
def by_sr(cls, sr, _update=False):
|
||||
rel_ids = cls.by_sr_cache(sr._id, _update=_update)
|
||||
adsrs = AdSR._byID_rel(rel_ids, data=True, eager_load=True,
|
||||
thing_data=True, return_dict = False)
|
||||
return adsrs
|
||||
|
||||
@classmethod
|
||||
def by_sr_merged(cls, sr, _update=False):
|
||||
if sr.name == g.default_sr:
|
||||
return cls.by_sr(sr)
|
||||
|
||||
my_adsrs = cls.by_sr(sr)
|
||||
global_adsrs = cls.by_sr(Subreddit._by_name(g.default_sr, stale=True))
|
||||
|
||||
seen = {}
|
||||
for adsr in my_adsrs:
|
||||
seen[adsr._thing1.codename] = True
|
||||
for adsr in global_adsrs:
|
||||
if adsr._thing1.codename not in seen:
|
||||
my_adsrs.append(adsr)
|
||||
|
||||
return my_adsrs
|
||||
|
||||
@classmethod
|
||||
def by_ad_and_sr(cls, ad, sr):
|
||||
q = cls._fast_query(ad, sr, "adsr")
|
||||
return q.values()[0]
|
||||
@@ -1,70 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
|
||||
<form action="/post/assignad" method="post" class="pretty-form medium-text"
|
||||
onsubmit="return post_form(this, 'assignad');">
|
||||
|
||||
<input type="hidden" name="fullname" value="${thing.ad._fullname}" />
|
||||
|
||||
<table class="lined-table borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<img src="${thing.ad.imgurl}"/>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/admin/ads/#${thing.ad.codename}">
|
||||
<h1 class="centered">${thing.ad.codename}</h1>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
community
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="community" /><br/>
|
||||
${error_field("SUBREDDIT_REQUIRED", "community", "span")}
|
||||
${error_field("SUBREDDIT_NOEXIST", "community", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
weight
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="weight" value="${thing.weight}" /><br/>
|
||||
${error_field("BAD_NUMBER", "weight", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button class="btn" type="submit">assign</button>
|
||||
|
||||
<span class="status"></span>
|
||||
|
||||
<p>
|
||||
<a href="/admin/ads">back to ads</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
|
||||
<%def name="adbuttons(codename, submit_link)">
|
||||
<ul class="flat-list buttons">
|
||||
%if codename != 'DART':
|
||||
<li><a href="#"
|
||||
onclick="$(this).parents('td').find('form').toggle(); return false;">
|
||||
edit</a></li>
|
||||
<li><a href="${submit_link}">submit</a></li>
|
||||
%endif
|
||||
<li><a href="/admin/ads/${codename}/assign">assign_SR</a></li>
|
||||
<li><a href="/admin/ads/${codename}/srs">view_SRs</a></li>
|
||||
</ul>
|
||||
</%def>
|
||||
|
||||
<%def name="adedit(fullname, codename='', imgurl='', raw_html='', linkurl='')">
|
||||
<form action="/post/editad" method="post" class="pretty-form medium-text"
|
||||
style="display:none"
|
||||
onsubmit="return post_form(this, 'editad');" id="adedit-${fullname}">
|
||||
<input type="hidden" name="fullname" value="${fullname}" />
|
||||
|
||||
<table class="lined-table borderless">
|
||||
<tr>
|
||||
<td>codename</td>
|
||||
<td>
|
||||
<input type="text" name="codename" value="${codename}" />
|
||||
${error_field("NO_TEXT", "codename", "span")}
|
||||
${error_field("INVALID_OPTION", "codename", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>img url</td>
|
||||
<td>
|
||||
<input type="text" name="imgurl" value="${imgurl}" />
|
||||
${error_field("NO_TEXT", "imgurl", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>raw html (overrides image)</td>
|
||||
<td>
|
||||
<textarea name="raw_html">
|
||||
${raw_html}
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>link url</td>
|
||||
<td>
|
||||
<input type="text" name="linkurl" value="${linkurl}" />
|
||||
${error_field("NO_TEXT", "linkurl", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button class="savebutton" type="submit">save</button>
|
||||
<span class="status"></span>
|
||||
</form>
|
||||
</%def>
|
||||
|
||||
<table class="lined-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>fn</th>
|
||||
<th>cn</th>
|
||||
<th>img</th>
|
||||
<th>links & buttons</th>
|
||||
</tr>
|
||||
%for ad in thing.ads:
|
||||
<tr>
|
||||
<td>${ad._fullname}</td>
|
||||
<td>${ad.codename}</td>
|
||||
%if ad.codename == "DART":
|
||||
<td class="centered">
|
||||
${unsafe(ad.rendering())}
|
||||
</td>
|
||||
<td class="entry">
|
||||
${adbuttons(ad.codename, ad.submit_link())}
|
||||
</td>
|
||||
%else:
|
||||
<td>
|
||||
<a name="${ad.codename}" href="${ad.linkurl}">
|
||||
${unsafe(ad.rendering())}
|
||||
</a>
|
||||
</td>
|
||||
<td class="entry">
|
||||
img: <a href="${ad.imgurl}">${ad.imgurl}</a><br/>
|
||||
link: <a href="${ad.linkurl}">${ad.linkurl}</a><br/>
|
||||
raw html: ${ad.raw_html}<br/>
|
||||
<br/>
|
||||
${adbuttons(ad.codename, ad.submit_link())}
|
||||
${adedit(ad._fullname, ad.codename, ad.imgurl, ad.raw_html, ad.linkurl)}
|
||||
</td>
|
||||
%endif
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
<button onclick="$('#adedit-NEW').show()">new ad</button>
|
||||
|
||||
${adedit("NEW")}
|
||||
@@ -1,71 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<%namespace file="utils.html" import="percentage"/>
|
||||
|
||||
<%def name="adsrline(adsr)">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/r/${adsr._thing2.name}/about/ads#${thing.ad.codename}">
|
||||
${adsr._thing2.name}
|
||||
</a>
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
${adsr.weight}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
${percentage(adsr.weight, thing.sr_totals[adsr._thing2.name])}
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<div style="float: right">
|
||||
<a href="${thing.ad.linkurl}">
|
||||
<img src="${thing.ad.imgurl}"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a href="/admin/ads/#${thing.ad.codename}">
|
||||
<h1>${thing.ad.codename}</h1>
|
||||
</a>
|
||||
|
||||
<table class="lined-table">
|
||||
<tr>
|
||||
<th>
|
||||
community
|
||||
</th>
|
||||
<th>
|
||||
wt
|
||||
</th>
|
||||
<th>
|
||||
pct
|
||||
</th>
|
||||
</tr>
|
||||
%for adsr in thing.adsrs:
|
||||
${adsrline(adsr)}
|
||||
%endfor
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="/admin/ads">back to ads</a>
|
||||
</p>
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
#ad {
|
||||
background-color: white; width: 100%; text-align: center;
|
||||
font: normal small verdana, arial, sans-serif}
|
||||
#adlink {
|
||||
background-color: white; width: 100%; text-align: center;
|
||||
font: normal small verdana, arial, sans-serif}
|
||||
#adlink {width: 100%; text-align: center;}
|
||||
#adlink a {color: #336699; text-decoration: none}
|
||||
#adlink a:hover {text-decoration: underline}
|
||||
</style>
|
||||
<script language="javascript" type="text/javascript">
|
||||
//<![CDATA[
|
||||
function rwt(a) {
|
||||
var ad = document.getElementById("ad")
|
||||
var s = ad.getElementsByTagName("embed")[0]
|
||||
if (!s) {
|
||||
var iframes = ad.getElementsByTagName("iframe");
|
||||
for(var i = 0; i < iframes.length; i++) {
|
||||
/* if pubmatic. there will be a tracking beacon with a unique ID*/
|
||||
if (iframes[i].getAttribute('name') == 'pbeacon') {
|
||||
var url = iframes[i].src;
|
||||
url = url.split('?');
|
||||
if (url[1]) {
|
||||
var x = url[1].split('&');
|
||||
for(var j = 0; j < x.length; j++) {
|
||||
var y = x[j].split('=');
|
||||
if (y[0] == 'adId') {
|
||||
s = url[0] + "?" + x[j];
|
||||
a.href = "http://ads.reddit.com/submit?url=" + encodeURIComponent(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(iframes[i].offsetHeight > 100 &&
|
||||
iframes[i].offsetWidth > 100) {
|
||||
s = iframes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s) s = ad.getElementsByTagName("img")[0]
|
||||
if (s) a.href = "http://ads.reddit.com/submit?url=" + encodeURIComponent(s.src);
|
||||
else a.target = ""
|
||||
return true;
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
</head>
|
||||
<body style="padding: 0; margin: 0;">
|
||||
<div id="ad">
|
||||
<!-- begin ad tag (tile=1) -->
|
||||
<script language="JavaScript" type="text/javascript">
|
||||
//<![CDATA[
|
||||
ord=Math.random()*10000000000000000;
|
||||
document.write('<script language="JavaScript" src="http://ad.doubleclick.net/adj/${thing.dartsite}/${thing.tag};kw=${thing.keyword};tile=1;sz=300x250;ord=' + ord + '?" type="text/javascript"></scr' + 'ipt>');
|
||||
//]]>
|
||||
</script>
|
||||
<noscript><a href="http://ad.doubleclick.net/jump/${thing.dartsite}/${thing.tag};kw=${thing.keyword};tile=1;sz=300x250;ord=123456789?" target="_blank"><img src="http://ad.doubleclick.net/ad/${thing.dartsite}/${thing.tag};kw=${thing.keyword};tile=1;sz=300x250;ord=123456789?" width="300" height="250" border="0" alt=""/></a></noscript>
|
||||
</div>
|
||||
<div id="adlink">
|
||||
<a target="_top" href="#" onmousedown="rwt(this)">
|
||||
${_("discuss this ad on reddit")}
|
||||
</a>
|
||||
</div>
|
||||
<script language="JavaScript" type="text/javascript">
|
||||
//<![CDATA[
|
||||
document.write("<img alt='' src='${unsafe(thing.tracker_url)}&random=" + Math.random()*10000000000000000 + "'/>");
|
||||
//]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,58 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
#ad {
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font: normal small verdana, arial, sans-serif;
|
||||
}
|
||||
#adlink {
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font: normal small verdana, arial, sans-serif;
|
||||
}
|
||||
#adlink {width: 100%; text-align: center;}
|
||||
#adlink a {color: #336699; text-decoration: none}
|
||||
#adlink a:hover {text-decoration: underline}
|
||||
#ad a img {border: none}
|
||||
</style>
|
||||
</head>
|
||||
<body style="padding: 0; margin: 0;">
|
||||
<div id="ad">
|
||||
<a target="_top" href="${thing.linkurl}">
|
||||
${unsafe(thing.rendering)}
|
||||
</a>
|
||||
</div>
|
||||
<div id="adlink">
|
||||
<a target="_top" href="${thing.submit_link}">
|
||||
${_("discuss this ad on reddit")}
|
||||
</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,130 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
<%namespace file="utils.html" import="percentage"/>
|
||||
|
||||
<%def name="row(ad, weight, whence)">
|
||||
|
||||
<tr class="${whence}">
|
||||
<td class="centered">
|
||||
<a href="${ad.linkurl}">
|
||||
<img src="${ad.imgurl}" alt="(place img here)"/>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td class="details">
|
||||
<div class="codename">
|
||||
<a href="/admin/ads/${ad.codename}">
|
||||
${ad.codename}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form action="/post/assignad" method="post"
|
||||
id="${ad.codename}" class="assign-form"
|
||||
onsubmit="return post_form(this, 'assignad');">
|
||||
|
||||
<div>
|
||||
<input type="hidden" name="fullname" value="${ad._fullname}" />
|
||||
<input type="hidden" name="community" value="${thing.sr_name}" />
|
||||
|
||||
%if whence == "inherited":
|
||||
weight: 
|
||||
<input type="text" name="weight" class="weight" />
|
||||
<div class="whence">
|
||||
(inheriting ${weight} from /r/${g.default_sr})
|
||||
</div>
|
||||
%else:
|
||||
weight: 
|
||||
<input
|
||||
%if thing.sr_name == g.default_sr and ad.codename == 'DART':
|
||||
disabled="disabled"
|
||||
%endif
|
||||
type="text" name="weight" class="weight" value="${weight}" /><br/>
|
||||
<div class="whence">
|
||||
%if whence == "overridden":
|
||||
|
||||
## pass
|
||||
|
||||
%elif thing.sr_name == g.default_sr:
|
||||
(hereby the sitewide default)
|
||||
%else:
|
||||
(overridden for /r/${thing.sr_name})
|
||||
%endif
|
||||
</div>
|
||||
%endif
|
||||
${error_field("BAD_NUMBER", "weight", "span")}
|
||||
</div>
|
||||
|
||||
%if whence != "unused":
|
||||
<div>
|
||||
percentage: 
|
||||
${percentage(weight, thing.total)}
|
||||
</div>
|
||||
%endif
|
||||
|
||||
<br/>
|
||||
|
||||
<div>
|
||||
%if thing.sr_name == g.default_sr and ad.codename == 'DART':
|
||||
<button disabled="disabled">save</button>
|
||||
%else:
|
||||
<button class="savebutton" type="submit">save</button>
|
||||
<span class="status"></span>
|
||||
%endif
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<table class="sr-ad-table lined-table">
|
||||
%if thing.adsrs:
|
||||
<tr>
|
||||
<td colspan="2" class="centered">
|
||||
Ads in rotation on /r/${thing.sr_name}:
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
|
||||
%for adsr in thing.adsrs:
|
||||
${row(adsr._thing1, adsr.weight,
|
||||
"overridden" if adsr._thing2.name == thing.sr_name else "inherited")}
|
||||
%endfor
|
||||
|
||||
%if thing.other_ads:
|
||||
<tr>
|
||||
<td colspan="2" class="centered">
|
||||
Ads not (yet) used on this subreddit:
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
|
||||
%for ad in thing.other_ads:
|
||||
${row(ad, "", "unused")}
|
||||
%endfor
|
||||
</table>
|
||||
|
||||
<button onclick="$('.assign-form .savebutton').click();">
|
||||
save all
|
||||
</button>
|
||||
Reference in New Issue
Block a user