Rssdit is an open source project powered by Assembla

Assembla offers free public and private SVN/Git repositories and project hosting with bug/issue tracking and collaboration tools.

Commit bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3

User picture
  • Commiter: shuffman
  • Author: shuffman
  • Parent: 7bf5fab1fa
  • In branches: master
  • 2008-07-02 17:37 (almost 6 years ago)

add media features

Files Affected

 
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
52
admins = 
52
admins = 
53
page_cache_time = 30
53
page_cache_time = 30
54
static_path = /static/
54
static_path = /static/
 
 
55
useragent = Mozilla/5.0 (compatible; bot/1.0; ChangeMe)
55
 
56
 
56
solr_url =  
57
solr_url =  
57
 
58
 
58
SECRET    = abcdefghijklmnopqrstuvwxyz0123456789
59
SECRET    = abcdefghijklmnopqrstuvwxyz0123456789
59
MODSECRET = abcdefghijklmnopqrstuvwxyz0123456789
60
MODSECRET = abcdefghijklmnopqrstuvwxyz0123456789
60
ip_hash = 
61
ip_hash = 
 
 
62
S3KEY_ID = ABCDEFGHIJKLMNOP1234
 
 
63
S3SECRET_KEY = aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCd
 
 
64
s3_thumb_bucket = /your.bucket.here/
 
 
65
default_thumb = /static/noimage.png
61
 
66
 
62
MIN_DOWN_LINK = 0
67
MIN_DOWN_LINK = 0
63
MIN_UP_KARMA  = 0
68
MIN_UP_KARMA  = 0
...
 
...
 
69
MODWINDOW = 2
74
MODWINDOW = 2
70
HOT_PAGE_AGE = 1
75
HOT_PAGE_AGE = 1
71
 
76
 
 
 
77
#
 
 
78
media_period  = 10 minutes
 
 
79
rising_period = 12 hours
 
 
80
 
72
# time of ratelimit purgatory (min)
81
# time of ratelimit purgatory (min)
73
RATELIMIT = 10
82
RATELIMIT = 10
74
 
83
 
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
657
              ad_file = nop("ad_file"),
657
              ad_file = nop("ad_file"),
658
              sr = VByName('sr'),
658
              sr = VByName('sr'),
659
              over_18 = VBoolean('over_18'),
659
              over_18 = VBoolean('over_18'),
 
 
660
              show_media = VBoolean('show_media'),
660
              type = VOneOf('type', ('public', 'private', 'restricted'))
661
              type = VOneOf('type', ('public', 'private', 'restricted'))
661
              )
662
              )
662
    def POST_site_admin(self, res, name ='', sr = None, **kw):
663
    def POST_site_admin(self, res, name ='', sr = None, **kw):
...
 
...
 
665
        kw = dict((k, v) for k, v in kw.iteritems()
666
        kw = dict((k, v) for k, v in kw.iteritems()
666
                  if v is not None
667
                  if v is not None
667
                  and k in ('name', 'title', 'description', 'firsttext',
668
                  and k in ('name', 'title', 'description', 'firsttext',
668
                            'static_path', 'ad_file', 'over_18',
669
                            'static_path', 'ad_file', 'over_18', 'show_media',
669
                            'type', 'header', 'lang', 'stylesheet'))
670
                            'type', 'header', 'lang', 'stylesheet'))
670
 
671
 
671
        #if a user is banned, return rate-limit errors
672
        #if a user is banned, return rate-limit errors
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
177
        o_links, pos = organic.organic_links(c.user)
177
        o_links, pos = organic.organic_links(c.user)
178
        if o_links:
178
        if o_links:
179
            # get links in proximity to pos
179
            # get links in proximity to pos
180
            disp_links = [o_links[(i + pos) % len(o_links)] for i in xrange(-2, 8)]
180
            l = min(len(o_links) - 3, 8)
 
 
181
            disp_links = [o_links[(i + pos) % len(o_links)] for i in xrange(-2, l)]
181
 
182
 
182
            b = IDBuilder(disp_links,
183
            b = IDBuilder(disp_links,
183
                          wrap = self.builder_wrapper)
184
                          wrap = self.builder_wrapper)
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
59
              pref_over_18 = VBoolean('over_18'),
59
              pref_over_18 = VBoolean('over_18'),
60
              pref_numsites = VInt('numsites', 1, 100),
60
              pref_numsites = VInt('numsites', 1, 100),
61
              pref_lang = VLang('lang'),
61
              pref_lang = VLang('lang'),
 
 
62
              pref_media = VOneOf('media', ('on', 'off', 'subreddit')),
62
              pref_compress = VBoolean('compress'),
63
              pref_compress = VBoolean('compress'),
63
              pref_min_link_score = VInt('min_link_score', -100, 100),
64
              pref_min_link_score = VInt('min_link_score', -100, 100),
64
              pref_min_comment_score = VInt('min_comment_score', -100, 100),
65
              pref_min_comment_score = VInt('min_comment_score', -100, 100),
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
21
################################################################################
21
################################################################################
22
from __future__ import with_statement
22
from __future__ import with_statement
23
from pylons import config
23
from pylons import config
24
import pytz, os
24
import pytz, os, logging, sys
25
from datetime import timedelta
25
from datetime import timedelta
26
from r2.lib.cache import LocalCache, Memcache, CacheChain
26
from r2.lib.cache import LocalCache, Memcache, CacheChain
27
from r2.lib.db.stats import QueryStats
27
from r2.lib.db.stats import QueryStats
...
 
...
 
146
                    full_name = os.path.join(log_path, fname)
146
                    full_name = os.path.join(log_path, fname)
147
                    os.remove(full_name)
147
                    os.remove(full_name)
148
 
148
 
 
 
149
        #setup the logger
 
 
150
        self.log = logging.getLogger('reddit')
 
 
151
        if self.debug:
 
 
152
            self.log.setLevel(logging.DEBUG)
 
 
153
            self.log.addHandler(logging.StreamHandler())
 
 
154
 
149
    def __del__(self):
155
    def __del__(self):
150
        """
156
        """
151
        Put any cleanup code to be run when the application finally exits 
157
        Put any cleanup code to be run when the application finally exits 
152
        here.
158
        here.
153
        """
159
        """
154
        pass
160
        pass
 
 
161
 
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
525
            try:
525
            try:
526
                for key in server_keys[server]: # These are mangled keys
526
                for key in server_keys[server]: # These are mangled keys
527
                    store_info = self._val_to_store_info(mapping[prefixed_to_orig_key[key]], min_compress_len)
527
                    store_info = self._val_to_store_info(mapping[prefixed_to_orig_key[key]], min_compress_len)
 
 
528
                    if not store_info:
 
 
529
                        continue
528
                    write("set %s %d %d %d\r\n%s\r\n" % (key, store_info[0], time, store_info[1], store_info[2]))
530
                    write("set %s %d %d %d\r\n%s\r\n" % (key, store_info[0], time, store_info[1], store_info[2]))
529
                server.send_cmds(''.join(bigcmd))
531
                server.send_cmds(''.join(bigcmd))
530
            except socket.error, msg:
532
            except socket.error, msg:
...
 
...
 
568
            min_compress_len = 0
570
            min_compress_len = 0
569
        else:
571
        else:
570
            flags |= Client._FLAG_PICKLE
572
            flags |= Client._FLAG_PICKLE
571
            val = pickle.dumps(val, 0)  # Ack! JLR hacks it so that LinkedDict unpicling works w/o figuring out __reduce__.
573
            val = pickle.dumps(val, -1)  # Ack! JLR hacks it so that LinkedDict unpicling works w/o figuring out __reduce__.
572
 
574
 
573
        #  silently do not store if value length exceeds maximum
575
        #  silently do not store if value length exceeds maximum
574
        if len(val) >= SERVER_MAX_VALUE_LENGTH: return(0)
576
        if len(val) >= SERVER_MAX_VALUE_LENGTH:
 
 
577
            return (0)
575
 
578
 
576
        lv = len(val)
579
        lv = len(val)
577
        # We should try to compress if min_compress_len > 0 and we could import zlib and this string is longer than our min threshold.
580
        # We should try to compress if min_compress_len > 0 and we could import zlib and this string is longer than our min threshold.
...
 
...
 
593
        self._statlog(cmd)
596
        self._statlog(cmd)
594
 
597
 
595
        store_info = self._val_to_store_info(val, min_compress_len)
598
        store_info = self._val_to_store_info(val, min_compress_len)
 
 
599
        if not store_info:
 
 
600
            return 0
596
 
601
 
597
        fullcmd = "%s %s %d %d %d\r\n%s" % (cmd, key, store_info[0], time, store_info[1], store_info[2])
602
        fullcmd = "%s %s %d %d %d\r\n%s" % (cmd, key, store_info[0], time, store_info[1], store_info[2])
598
        try:
603
        try:
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
22
 
22
 
23
from r2.models import Link, Subreddit
23
from r2.models import Link, Subreddit
24
from r2.lib import utils
24
from r2.lib import utils
 
 
25
from pylons import g
 
 
26
 
 
 
27
count_period = g.rising_period
25
 
28
 
26
#stubs
29
#stubs
27
 
30
 
28
def incr_counts(wrapped):
31
def incr_counts(wrapped):
29
    pass
32
    pass
30
 
33
 
31
def get_link_counts(period = '12 hours'):
34
def get_link_counts(period = count_period):
32
    links = Link._query(Link.c._date >= utils.timeago(period),
35
    links = Link._query(Link.c._date >= utils.timeago(period),
33
                        limit=50, data = True)
36
                        limit=50, data = True)
34
    return dict((l._fullname, (0, l.sr_id)) for l in links)
37
    return dict((l._fullname, (0, l.sr_id)) for l in links)
35
 
38
 
36
def get_sr_counts(period = '12 hours'):
39
def get_sr_counts(period = count_period):
37
    srs = Subreddit._query()
40
    srs = Subreddit._query()
38
    return dict((l._fullname, (0, l.sr_id)) for l in links)
41
    return dict((l._fullname, (0, l.sr_id)) for l in links)
39
 
42
 
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
 
 
1
# The contents of this file are subject to the Common Public Attribution
 
 
2
# License Version 1.0. (the "License"); you may not use this file except in
 
 
3
# compliance with the License. You may obtain a copy of the License at
 
 
4
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
 
 
5
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
 
 
6
# software over a computer network and provide for limited attribution for the
 
 
7
# Original Developer. In addition, Exhibit A has been modified to be consistent
 
 
8
# with Exhibit B.
 
 
9
# 
 
 
10
# Software distributed under the License is distributed on an "AS IS" basis,
 
 
11
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 
 
12
# the specific language governing rights and limitations under the License.
 
 
13
# 
 
 
14
# The Original Code is Reddit.
 
 
15
# 
 
 
16
# The Original Developer is the Initial Developer.  The Initial Developer of the
 
 
17
# Original Code is CondeNet, Inc.
 
 
18
# 
 
 
19
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
 
 
20
# CondeNet, Inc. All Rights Reserved.
 
 
21
################################################################################
 
 
22
 
 
 
23
from pylons import g, config
 
 
24
 
 
 
25
from r2.models.link import Link
 
 
26
from r2.lib.workqueue import WorkQueue
 
 
27
from r2.lib import s3cp
 
 
28
from r2.lib.utils import timeago, fetch_things2
 
 
29
from r2.lib.db.operators import desc
 
 
30
from r2.lib.scraper import make_scraper
 
 
31
 
 
 
32
import tempfile
 
 
33
from Queue import Queue
 
 
34
 
 
 
35
s3_thumbnail_bucket = g.s3_thumb_bucket
 
 
36
media_period = g.media_period
 
 
37
threads = 20
 
 
38
log = g.log
 
 
39
 
 
 
40
def thumbnail_url(link):
 
 
41
    """Given a link, returns the url for its thumbnail based on its fullname"""
 
 
42
    return 'http:/%s%s.png' % (s3_thumbnail_bucket, link._fullname)
 
 
43
 
 
 
44
def upload_thumb(link, image):
 
 
45
    """Given a link and an image, uploads the image to s3 into an image
 
 
46
    based on the link's fullname"""
 
 
47
    f = tempfile.NamedTemporaryFile(suffix = '.png')
 
 
48
    image.save(f)
 
 
49
 
 
 
50
    resource = s3_thumbnail_bucket + link._fullname + '.png'
 
 
51
    log.debug('uploading to s3: %s' % link._fullname)
 
 
52
    s3cp.send_file(f.name, resource, 'image/png', 'public-read', None, False)
 
 
53
    log.debug('thumbnail %s: %s' % (link._fullname, thumbnail_url(link)))
 
 
54
 
 
 
55
def make_link_info_job(results, link, useragent):
 
 
56
    """Returns a unit of work to send to a work queue that downloads a
 
 
57
    link's thumbnail and media object. Places the result in the results
 
 
58
    dict"""
 
 
59
    def job():
 
 
60
        scraper = make_scraper(link.url)
 
 
61
 
 
 
62
        thumbnail = scraper.thumbnail()
 
 
63
        media_object = scraper.media_object()
 
 
64
 
 
 
65
        if thumbnail:
 
 
66
            upload_thumb(link, thumbnail)
 
 
67
 
 
 
68
        results[link] = (thumbnail, media_object)
 
 
69
    return job
 
 
70
 
 
 
71
def update_link(link, thumbnail, media_object):
 
 
72
    """Sets the link's has_thumbnail and media_object attributes iin the
 
 
73
    database."""
 
 
74
    if thumbnail:
 
 
75
        link.has_thumbnail = True
 
 
76
 
 
 
77
    if media_object:
 
 
78
        link.media_object = media_object
 
 
79
 
 
 
80
    link._commit()
 
 
81
 
 
 
82
def process_new_links(period = media_period, force = False):
 
 
83
    """Fetches links from the last period and sets their media
 
 
84
    properities. If force is True, it will fetch properities for links
 
 
85
    even if the properties already exist"""
 
 
86
    links = Link._query(Link.c._date > timeago(period), sort = desc('_date'),
 
 
87
                        data = True)
 
 
88
    results = {}
 
 
89
    jobs = []
 
 
90
    for link in fetch_things2(links):
 
 
91
        if link.is_self:
 
 
92
            continue
 
 
93
 
 
 
94
        if not force and (link.has_thumbnail or link.media_object):
 
 
95
            continue
 
 
96
 
 
 
97
        jobs.append(make_link_info_job(results, link, g.useragent))
 
 
98
 
 
 
99
    #send links to a queue
 
 
100
    wq = WorkQueue(jobs, num_workers = 20)
 
 
101
    wq.start()
 
 
102
    wq.jobs.join()
 
 
103
 
 
 
104
    #when the queue is finished, do the db writes in this thread
 
 
105
    for link, info in results.items():
 
 
106
        update_link(link, info[0], info[1])
 
 
107
 
 
 
108
def set_media(link):
 
 
109
    """Sets the media properties for a single link."""
 
 
110
    results = {}
 
 
111
    make_link_info_job(results, link, g.useragent)()
 
 
112
    update_link(link, *results[link])
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
 
 
1
#!/usr/bin/env python
 
 
2
 
 
 
3
# The contents of this file are subject to the Common Public Attribution
 
 
4
# License Version 1.0. (the "License"); you may not use this file except in
 
 
5
# compliance with the License. You may obtain a copy of the License at
 
 
6
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
 
 
7
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
 
 
8
# software over a computer network and provide for limited attribution for the
 
 
9
# Original Developer. In addition, Exhibit A has been modified to be consistent
 
 
10
# with Exhibit B.
 
 
11
# 
 
 
12
# Software distributed under the License is distributed on an "AS IS" basis,
 
 
13
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 
 
14
# the specific language governing rights and limitations under the License.
 
 
15
# 
 
 
16
# The Original Code is Reddit.
 
 
17
# 
 
 
18
# The Original Developer is the Initial Developer.  The Initial Developer of the
 
 
19
# Original Code is CondeNet, Inc.
 
 
20
# 
 
 
21
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
 
 
22
# CondeNet, Inc. All Rights Reserved.
 
 
23
################################################################################
 
 
24
 
 
 
25
import base64, hmac, sha, os, sys, getopt
 
 
26
from datetime import datetime
 
 
27
from pylons import g,config
 
 
28
 
 
 
29
KEY_ID = g.S3KEY_ID
 
 
30
SECRET_KEY = g.S3SECRET_KEY
 
 
31
 
 
 
32
class S3Exception(Exception): pass
 
 
33
 
 
 
34
def make_header(verb, date, amz_headers, resource, content_type):
 
 
35
    content_md5 = ''
 
 
36
 
 
 
37
    #amazon headers
 
 
38
    lower_head = dict((key.lower(), val)
 
 
39
                      for key, val in amz_headers.iteritems())
 
 
40
    keys = lower_head.keys()
 
 
41
    keys.sort()
 
 
42
    amz_lst = ['%s:%s' % (key, lower_head[key]) for key in keys]
 
 
43
    amz_str = '\n'.join(amz_lst)
 
 
44
 
 
 
45
    s = '\n'.join((verb,
 
 
46
                   content_md5,
 
 
47
                   content_type,
 
 
48
                   date,
 
 
49
                   amz_str,
 
 
50
                   resource))
 
 
51
 
 
 
52
    h = hmac.new(SECRET_KEY, s, sha)
 
 
53
    return base64.encodestring(h.digest()).strip()
 
 
54
 
 
 
55
def send_file(filename, resource, content_type, acl, rate, meter):
 
 
56
    date = datetime.utcnow().strftime("%a, %d %b %Y %X GMT")
 
 
57
    amz_headers = {'x-amz-acl': acl}
 
 
58
 
 
 
59
    auth_header = make_header('PUT', date, amz_headers, resource, content_type)
 
 
60
 
 
 
61
    params = ['-T', filename,
 
 
62
              '-H', 'x-amz-acl: %s' % amz_headers['x-amz-acl'],
 
 
63
              '-H', 'Authorization: AWS %s:%s' % (KEY_ID, auth_header),
 
 
64
              '-H', 'Date: %s' % date]
 
 
65
 
 
 
66
    if content_type:
 
 
67
        params.append('-H')
 
 
68
        params.append('Content-Type: %s' % content_type)
 
 
69
 
 
 
70
    if rate:
 
 
71
        params.append('--limit-rate')
 
 
72
        params.append(rate)
 
 
73
 
 
 
74
    if meter:
 
 
75
        params.append('-o')
 
 
76
        params.append('s3cp.output')
 
 
77
    else:
 
 
78
        params.append('-s')
 
 
79
 
 
 
80
    params.append('https://s3.amazonaws.com%s' % resource)
 
 
81
 
 
 
82
    exit_code = os.spawnlp(os.P_WAIT, 'curl', 'curl', *params)
 
 
83
    if exit_code:
 
 
84
        raise S3Exception(exit_code)
 
 
85
 
 
 
86
 
 
 
87
if __name__ == '__main__':
 
 
88
    options = "a:c:l:m"
 
 
89
    try:
 
 
90
        opts, args = getopt.getopt(sys.argv[1:], options)
 
 
91
    except:
 
 
92
        sys.exit(2)
 
 
93
 
 
 
94
    opts = dict(opts)
 
 
95
 
 
 
96
    send_file(args[0], args[1],
 
 
97
              opts.get('-c', ''),
 
 
98
              opts.get('-a', 'private'),
 
 
99
              opts.get('-l'),
 
 
100
              opts.has_key('-m'))
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
 
 
1
# The contents of this file are subject to the Common Public Attribution
 
 
2
# License Version 1.0. (the "License"); you may not use this file except in
 
 
3
# compliance with the License. You may obtain a copy of the License at
 
 
4
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
 
 
5
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
 
 
6
# software over a computer network and provide for limited attribution for the
 
 
7
# Original Developer. In addition, Exhibit A has been modified to be consistent
 
 
8
# with Exhibit B.
 
 
9
# 
 
 
10
# Software distributed under the License is distributed on an "AS IS" basis,
 
 
11
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 
 
12
# the specific language governing rights and limitations under the License.
 
 
13
# 
 
 
14
# The Original Code is Reddit.
 
 
15
# 
 
 
16
# The Original Developer is the Initial Developer.  The Initial Developer of the
 
 
17
# Original Code is CondeNet, Inc.
 
 
18
# 
 
 
19
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
 
 
20
# CondeNet, Inc. All Rights Reserved.
 
 
21
################################################################################
 
 
22
 
 
 
23
from pylons import g
 
 
24
from r2.lib import utils
 
 
25
from r2.lib.memoize import memoize
 
 
26
 
 
 
27
from urllib2 import Request, HTTPError, URLError, urlopen
 
 
28
import urlparse, re, urllib, logging, StringIO, logging
 
 
29
import Image, ImageFile
 
 
30
 
 
 
31
log = g.log
 
 
32
useragent = g.useragent
 
 
33
 
 
 
34
chunk_size = 1024
 
 
35
thumbnail_size = 70, 70
 
 
36
 
 
 
37
def image_to_str(image):
 
 
38
    s = StringIO.StringIO()
 
 
39
    image.save(s, image.format)
 
 
40
    s.seek(0)
 
 
41
    return s.read()
 
 
42
 
 
 
43
def str_to_image(s):
 
 
44
    s = StringIO.StringIO(s)
 
 
45
    s.seek(0)
 
 
46
    image = Image.open(s)
 
 
47
    return image
 
 
48
 
 
 
49
@memoize('media.fetch_url')
 
 
50
def fetch_url(url, referer = None, retries = 1, dimension = False):
 
 
51
    cur_try = 0
 
 
52
    #log.debug('fetching: %s' % url)
 
 
53
    nothing = None if dimension else (None, None)
 
 
54
    while True:
 
 
55
        try:
 
 
56
            req = Request(url)
 
 
57
            if useragent:
 
 
58
                req.add_header('User-Agent', useragent)
 
 
59
            if referer:
 
 
60
                req.add_header('Referer', referer)
 
 
61
 
 
 
62
            open_req = urlopen(req)
 
 
63
 
 
 
64
            #if we only need the dimension of the image, we may not
 
 
65
            #need the entire image
 
 
66
            if dimension:
 
 
67
                content = open_req.read(chunk_size)
 
 
68
            else:
 
 
69
                content = open_req.read()
 
 
70
            content_type = open_req.headers.get('content-type')
 
 
71
 
 
 
72
            if 'image' in content_type:
 
 
73
                p = ImageFile.Parser()
 
 
74
                new_data = content
 
 
75
                while not p.image and new_data:
 
 
76
                    p.feed(new_data)
 
 
77
                    new_data = open_req.read(chunk_size)
 
 
78
                    content += new_data
 
 
79
 
 
 
80
                #return the size, or return the data
 
 
81
                if dimension and p.image:
 
 
82
                    return p.image.size
 
 
83
                elif dimension:
 
 
84
                    return nothing
 
 
85
            elif dimension:
 
 
86
                #expected an image, but didn't get one
 
 
87
                return nothing
 
 
88
 
 
 
89
            return content_type, content
 
 
90
 
 
 
91
        except (URLError, HTTPError), e:
 
 
92
            cur_try += 1
 
 
93
            if cur_try >= retries:
 
 
94
                log.debug('error while fetching: %s referer: %s' % (url, referer))
 
 
95
                log.debug(e)
 
 
96
                return nothing
 
 
97
        finally:
 
 
98
            if 'open_req' in locals():
 
 
99
                open_req.close()
 
 
100
 
 
 
101
img_rx = re.compile(r'<\s*(?:img)[^>]*src\s*=\s*[\"\']?([^\"\'\s>]*)[^>]*', re.IGNORECASE | re.S) 
 
 
102
def image_urls(base_url, html):
 
 
103
    for match in img_rx.findall(html):
 
 
104
        image_url = urlparse.urljoin(base_url, match)
 
 
105
        yield image_url
 
 
106
 
 
 
107
class Scraper:
 
 
108
    def __init__(self, url):
 
 
109
        self.url = url
 
 
110
        self.content = None
 
 
111
        self.content_type = None
 
 
112
 
 
 
113
    def download(self):
 
 
114
        self.content_type, self.content = fetch_url(self.url)
 
 
115
 
 
 
116
    def largest_image_url(self):
 
 
117
        if not self.content:
 
 
118
            self.download()
 
 
119
 
 
 
120
        #if download didn't work
 
 
121
        if not self.content:
 
 
122
            return None
 
 
123
 
 
 
124
        max_area = 0
 
 
125
        max_url = None
 
 
126
 
 
 
127
        #if the original url was an image, use that
 
 
128
        if 'image' in self.content_type:
 
 
129
            urls = [self.url]
 
 
130
        else:
 
 
131
            urls = image_urls(self.url, self.content)
 
 
132
 
 
 
133
        for image_url in urls:
 
 
134
            size = fetch_url(image_url, referer = self.url, dimension = True)
 
 
135
            if not size:
 
 
136
                continue
 
 
137
 
 
 
138
            area = size[0] * size[1]
 
 
139
 
 
 
140
            #ignore little images
 
 
141
            if area < 5000:
 
 
142
                log.debug('ignore little %s' % image_url)
 
 
143
                continue
 
 
144
 
 
 
145
            #ignore excessively long/wide images
 
 
146
            if max(size) / min(size) > 1.5:
 
 
147
                log.debug('ignore dimensions %s' % image_url)
 
 
148
                continue
 
 
149
 
 
 
150
            if area > max_area:
 
 
151
                max_area = area
 
 
152
                max_url = image_url
 
 
153
 
 
 
154
        return max_url
 
 
155
 
 
 
156
    def thumbnail(self):
 
 
157
        image_url = self.largest_image_url()
 
 
158
        if image_url:
 
 
159
            content_type, image_str = fetch_url(image_url, referer = self.url)
 
 
160
            if image_str:
 
 
161
                image = str_to_image(image_str)
 
 
162
                image.thumbnail(thumbnail_size, Image.ANTIALIAS)
 
 
163
                return image
 
 
164
 
 
 
165
    def media_object(self):
 
 
166
        return None
 
 
167
 
 
 
168
youtube_rx = re.compile('.*v=([A-Za-z0-9-_]+).*')
 
 
169
 
 
 
170
class YoutubeScraper(Scraper):
 
 
171
    media_template = '<object width="425" height="350"><param name="movie"
value="http://www.youtube.com/v/%s"></param><param name="wmode" value="transparent"></param><embed
src="http://www.youtube.com/v/%s" type="application/x-shockwave-flash" wmode="transparent" width="425"
height="350"></embed></object>'
 
 
172
 
 
 
173
    def __init__(self, url):
 
 
174
        m = youtube_rx.match(url)
 
 
175
        if m:
 
 
176
            self.video_id = m.groups()[0]
 
 
177
        else:
 
 
178
            #if it's not a youtube video, just treat it like a normal page
 
 
179
            log.debug('reverting youtube to regular scraper: %s' % url)
 
 
180
            self.__class__ = Scraper
 
 
181
 
 
 
182
        Scraper.__init__(self, url)
 
 
183
 
 
 
184
    def largest_image_url(self):
 
 
185
         return 'http://img.youtube.com/vi/%s/default.jpg' % self.video_id
 
 
186
 
 
 
187
    def media_object(self):
 
 
188
        return self.media_template % (self.video_id, self.video_id)
 
 
189
 
 
 
190
gootube_rx = re.compile('.*videoplay\?docid=([A-Za-z0-9-_]+).*')
 
 
191
gootube_thumb_rx = re.compile(".*thumbnail:\s*\'(http://[^/]+/ThumbnailServer2[^\']+)\'.*", re.IGNORECASE | re.S)
 
 
192
 
 
 
193
class GootubeScraper(Scraper):
 
 
194
    media_template = '<embed style="width:400px; height:326px;" id="VideoPlayback" type="application/x-shockwave-flash"
src="http://video.google.com/googleplayer.swf?docId=%s&hl=en" flashvars=""> </embed>'
 
 
195
    def __init__(self, url):
 
 
196
        m = gootube_rx.match(url)
 
 
197
        if m:
 
 
198
            self.video_id = m.groups()[0]
 
 
199
        else:
 
 
200
            self.__class__ = Scraper
 
 
201
        Scraper.__init__(self, url)
 
 
202
 
 
 
203
    def largest_image_url(self):
 
 
204
        if not self.content:
 
 
205
            self.download()
 
 
206
 
 
 
207
        if not self.content:
 
 
208
            return None
 
 
209
 
 
 
210
        m = gootube_thumb_rx.match(self.content)
 
 
211
        if m:
 
 
212
            image_url = m.groups()[0]
 
 
213
            image_url = utils.safe_eval_str(image_url)
 
 
214
            return image_url
 
 
215
 
 
 
216
    def media_object(self):
 
 
217
        return self.media_template % self.video_id
 
 
218
 
 
 
219
scrapers = {'youtube.com': YoutubeScraper,
 
 
220
            'video.google.com': GootubeScraper}
 
 
221
 
 
 
222
youtube_in_google_rx = re.compile('.*<div class="original-text">.*href="(http://[^"]*youtube.com/watch[^"]+).*', re.S)
 
 
223
 
 
 
224
def make_scraper(url):
 
 
225
    scraper = scrapers.get(utils.domain(url), Scraper)
 
 
226
 
 
 
227
    #sometimes youtube scrapers masquerade as google scrapers
 
 
228
    if scraper == GootubeScraper:
 
 
229
        h = Scraper(url)
 
 
230
        h.download()
 
 
231
        m = youtube_in_google_rx.match(h.content)
 
 
232
        if m:
 
 
233
            youtube_url = m.groups()[0]
 
 
234
            log.debug('%s is really %s' % (url, youtube_url))
 
 
235
            url = youtube_url
 
 
236
            return make_scraper(url)
 
 
237
    return scraper(url)
 
 
238
 
 
 
239
def test():
 
 
240
    from r2.lib.pool2 import WorkQueue
 
 
241
    jobs = []
 
 
242
    f = open('/tmp/testurls.txt')
 
 
243
    for url in f:
 
 
244
        if url.startswith('#'):
 
 
245
            continue
 
 
246
        if url.startswith('/info'):
 
 
247
            continue
 
 
248
 
 
 
249
        def make_job(url):
 
 
250
            def fetch(url):
 
 
251
                print 'START', url
 
 
252
                url = url.strip()
 
 
253
                h = make_scraper(url)
 
 
254
                image_url = h.largest_image_url()
 
 
255
                print 'DONE', image_url
 
 
256
            return lambda: fetch(url)
 
 
257
 
 
 
258
        jobs.append(make_job(url))
 
 
259
 
 
 
260
    print jobs[0]()
 
 
261
    #wq = WorkQueue(jobs)
 
 
262
    #wq.start()            
 
 
263
 
 
 
264
if __name__ == '__main__':
 
 
265
    test()
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
742
 
742
 
743
def valid_vote_hash(hash, user, thing):
743
def valid_vote_hash(hash, user, thing):
744
    return True
744
    return True
 
 
745
 
 
 
746
def safe_eval_str(unsafe_str):
 
 
747
    return unsafe_str.replace('\\x3d', '=').replace('\\x26', '&')
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
 
 
1
# The contents of this file are subject to the Common Public Attribution
 
 
2
# License Version 1.0. (the "License"); you may not use this file except in
 
 
3
# compliance with the License. You may obtain a copy of the License at
 
 
4
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
 
 
5
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
 
 
6
# software over a computer network and provide for limited attribution for the
 
 
7
# Original Developer. In addition, Exhibit A has been modified to be consistent
 
 
8
# with Exhibit B.
 
 
9
# 
 
 
10
# Software distributed under the License is distributed on an "AS IS" basis,
 
 
11
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 
 
12
# the specific language governing rights and limitations under the License.
 
 
13
# 
 
 
14
# The Original Code is Reddit.
 
 
15
# 
 
 
16
# The Original Developer is the Initial Developer.  The Initial Developer of the
 
 
17
# Original Code is CondeNet, Inc.
 
 
18
# 
 
 
19
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
 
 
20
# CondeNet, Inc. All Rights Reserved.
 
 
21
################################################################################
 
 
22
 
 
 
23
from pylons import g
 
 
24
from Queue import Queue, Empty
 
 
25
from threading import Thread
 
 
26
from datetime import datetime, timedelta
 
 
27
import time
 
 
28
 
 
 
29
log = g.log
 
 
30
 
 
 
31
class WorkQueue(object):
 
 
32
    """A WorkQueue is a queue that takes a number of functions and runs
 
 
33
    them in parallel"""
 
 
34
 
 
 
35
    def __init__(self, jobs, num_workers = 5, timeout = 30):
 
 
36
        """Creates a WorkQueue that will process jobs with num_workers
 
 
37
        threads. If a job takes longer than timeout seconds to run, WorkQueue
 
 
38
        won't wait for it to finish before claiming to be finished."""
 
 
39
        self.jobs = Queue()
 
 
40
        self.work_count = Queue(num_workers)
 
 
41
        self.workers = {}
 
 
42
        self.timeout = timedelta(seconds = timeout)
 
 
43
 
 
 
44
        for j in jobs:
 
 
45
            self.jobs.put(j)
 
 
46
 
 
 
47
    def monitor(self):
 
 
48
        done = False
 
 
49
        while not done:
 
 
50
            if self.jobs.empty() and not self.workers:
 
 
51
                done = True
 
 
52
 
 
 
53
            for worker, start_time in self.workers.items():
 
 
54
                if (not worker.isAlive() or
 
 
55
                    datetime.now() - start_time > self.timeout): 
 
 
56
                    self.work_count.get_nowait()
 
 
57
                    self.jobs.task_done()
 
 
58
                    del self.workers[worker]
 
 
59
 
 
 
60
            time.sleep(1)
 
 
61
 
 
 
62
    def start(self):
 
 
63
        monitor_thread = Thread(target = self.monitor)
 
 
64
        monitor_thread.setDaemon(True)
 
 
65
        monitor_thread.start()
 
 
66
 
 
 
67
        while not self.jobs.empty():
 
 
68
            job = self.jobs.get()
 
 
69
 
 
 
70
            work_thread = Thread(target = job)
 
 
71
            work_thread.setDaemon(True)
 
 
72
            self.work_count.put(True)
 
 
73
            self.workers[work_thread] = datetime.now()
 
 
74
            work_thread.start()
 
 
75
 
 
 
76
if __name__ == '__main__':
 
 
77
    def make_job(n):
 
 
78
        import random, time
 
 
79
        def job():
 
 
80
            print 'starting %s' % n
 
 
81
            time.sleep(random.randint(1, 10))
 
 
82
            print 'ending %s' % n
 
 
83
        return job
 
 
84
 
 
 
85
    jobs = [make_job(n) for n in xrange(10)]
 
 
86
    wq = WorkQueue(jobs, timeout = 2)
 
 
87
    wq.start()
 
 
88
    wq.jobs.join()
 
 
89
    print 'DONE'
 
 
90
 
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
54
 
54
 
55
 
55
 
56
    def __repr__(self):
56
    def __repr__(self):
57
        return '<%s %s %s>' % (self.__class__.__name__,
57
        return '<%s %s>' % (self.__class__.__name__, self.lookups)
58
                               self.lookups, self.context)
 
 
59
 
58
 
60
 
59
 
61
    def template(self, style = 'html'):
60
    def template(self, style = 'html'):
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
58
                     spammer = 0,
58
                     spammer = 0,
59
                     sort_options = {},
59
                     sort_options = {},
60
                     has_subscribed = False,
60
                     has_subscribed = False,
 
 
61
                     pref_media = 'off',
61
                     )
62
                     )
62
 
63
 
63
    def karma(self, kind, sr = None):
64
    def karma(self, kind, sr = None):
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
45
                     reported = 0, num_comments = 0,
45
                     reported = 0, num_comments = 0,
46
                     moderator_banned = False,
46
                     moderator_banned = False,
47
                     banned_before_moderator = False,
47
                     banned_before_moderator = False,
 
 
48
                     media_object = None,
 
 
49
                     has_thumbnail = False,
48
                     ip = '0.0.0.0')
50
                     ip = '0.0.0.0')
49
 
51
 
50
    def __init__(self, *a, **kw):
52
    def __init__(self, *a, **kw):
...
 
...
 
201
                              wrapped.show_spam,
203
                              wrapped.show_spam,
202
                              wrapped.show_reports,
204
                              wrapped.show_reports,
203
                              wrapped.can_ban,
205
                              wrapped.can_ban,
 
 
206
                              wrapped.thumbnail,
204
                              wrapped.moderator_banned))
207
                              wrapped.moderator_banned))
205
        s = ''.join(s)
208
        s = ''.join(s)
206
        return s
209
        return s
...
 
...
 
216
    @classmethod
219
    @classmethod
217
    def add_props(cls, user, wrapped):
220
    def add_props(cls, user, wrapped):
218
        from r2.lib.count import incr_counts
221
        from r2.lib.count import incr_counts
 
 
222
        from r2.lib.media import thumbnail_url
 
 
223
 
219
        saved = Link._saved(user, wrapped) if user else {}
224
        saved = Link._saved(user, wrapped) if user else {}
220
        hidden = Link._hidden(user, wrapped) if user else {}
225
        hidden = Link._hidden(user, wrapped) if user else {}
221
        #clicked = Link._clicked(user, wrapped) if user else {}
226
        #clicked = Link._clicked(user, wrapped) if user else {}
...
 
...
 
223
 
228
 
224
        for item in wrapped:
229
        for item in wrapped:
225
 
230
 
 
 
231
            show_media = (c.user.pref_media == 'on' or
 
 
232
                          (c.user.pref_media == 'subreddit' and
 
 
233
                           item.subreddit.show_media))
 
 
234
 
 
 
235
            if not show_media:
 
 
236
                item.thumbnail = ""
 
 
237
            elif item.has_thumbnail:
 
 
238
                item.thumbnail = thumbnail_url(item)
 
 
239
            else:
 
 
240
                item.thumbnail = g.default_thumb
 
 
241
 
226
            item.score = max(0, item.score)
242
            item.score = max(0, item.score)
227
 
243
 
228
            item.domain = (domain(item.url) if not item.is_self
244
            item.domain = (domain(item.url) if not item.is_self
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
43
                     ad_file = os.path.join(g.static_path, 'ad_default.html'),
43
                     ad_file = os.path.join(g.static_path, 'ad_default.html'),
44
                     reported = 0,
44
                     reported = 0,
45
                     valid_votes = 0,
45
                     valid_votes = 0,
 
 
46
                     show_media = False,
46
                     )
47
                     )
47
 
48
 
48
    @classmethod
49
    @classmethod
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
131
    compute_height:function() {
131
    compute_height:function() {
132
        var arrows = this.$("arrows");
132
        var arrows = this.$("arrows");
133
        var entry  = this.$("entry");
133
        var entry  = this.$("entry");
 
 
134
        var thumb  = this.$("thumbnail");
134
        var num  = this.$("num");
135
        var num  = this.$("num");
135
        return Math.max(arrows ? arrows.offsetHeight : 0,
136
        return Math.max(arrows ? arrows.offsetHeight : 0,
136
                        entry  ? entry.offsetHeight  : 0,
137
                        entry  ? entry.offsetHeight  : 0,
 
 
138
                        thumb  ? thumb.offsetHeight  : 0,
137
                        num    ? num.offsetHeight    : 0);
139
                        num    ? num.offsetHeight    : 0);
138
    },
140
    },
139
 
141
 
...
 
...
 
151
 
153
 
152
    set_height: function(h) {
154
    set_height: function(h) {
153
        var entry = this.$('entry');
155
        var entry = this.$('entry');
154
        var arrows   = this.$('arrows');
156
        var thumb = this.$('thumbnail');
 
 
157
        var arrows = this.$('arrows');
155
        var num   = this.$('num');
158
        var num   = this.$('num');
156
        if(h == "fit" ||
159
        if(h == "fit" ||
157
           (this.max_height() && h >= this.max_height() *.90 )) {
160
           (this.max_height() && h >= this.max_height() *.90 )) {
...
 
...
 
190
        }
193
        }
191
        entry.style.height = h;
194
        entry.style.height = h;
192
        if(arrows) { arrows.style.height = h; }
195
        if(arrows) { arrows.style.height = h; }
 
 
196
        if(thumb)  { thumb.style.height = h; }
193
        if(num) { 
197
        if(num) { 
194
            if (h) 
198
            if (h) 
195
                num.style.marginTop = 0;
199
                num.style.marginTop = 0;
...
 
...
 
457
    return _global_fetching_tag;
461
    return _global_fetching_tag;
458
}
462
}
459
 
463
 
460
function setClick(a) {
464
function setClick(a, css_class) {
 
 
465
    css_class = css_class || "title";
461
    var id = _id(a);
466
    var id = _id(a);
462
    if (id) {
467
    if (id) {
463
        if(logged) {
468
        if(logged) {
464
            a.className = "title loggedin click";
469
            a.className = css_class + " loggedin click";
465
        }
470
        }
466
        else {
471
        else {
467
            a.className = "title click";
472
            a.className = css_class + " click";
468
        }
473
        }
469
        setClickCookie(id);
474
        setClickCookie(id);
470
    }
475
    }
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
26
h2 a:hover { text-decoration: underline }
26
h2 a:hover { text-decoration: underline }
27
h3 { font-size:110%; /*text-transform:uppercase;*/ }
27
h3 { font-size:110%; /*text-transform:uppercase;*/ }
28
 
28
 
29
a img { border:none }
29
a img { border: 0 none; }
30
a { text-decoration: none; color: #369; }
30
a { text-decoration: none; color: #369; }
31
 
31
 
 
 
32
/*
 
 
33
a:active { border: 0 none;}
 
 
34
a:focus { -moz-outline-style: none; }
 
 
35
*/
 
 
36
 
32
div.autosize { display: table; width: 1px}
37
div.autosize { display: table; width: 1px}
33
div.autosize > div { display: table-cell; }
38
div.autosize > div { display: table-cell; }
34
 
39
 
...
 
...
 
59
 
64
 
60
/* header / menus */
65
/* header / menus */
61
 
66
 
62
/*:-moz-any-link:focus { outline: none }*/
 
 
63
 
 
 
64
.hover a:hover { text-decoration: underline }
67
.hover a:hover { text-decoration: underline }
65
 
68
 
66
.selected { font-weight: bold; }
69
.selected { font-weight: bold; }
...
 
...
 
498
.tagline a.friend {color: orangered }
501
.tagline a.friend {color: orangered }
499
.tagline a:hover { text-decoration: underline }
502
.tagline a:hover { text-decoration: underline }
500
 
503
 
 
 
504
.watch-play { 
 
 
505
    background: transparent url(/static/reddit-button-play.gif) no-repeat scroll right center;
 
 
506
    padding-right: 15px;
 
 
507
    color: #336699;
 
 
508
}
 
 
509
.watch-stop {  
 
 
510
    color: red;
 
 
511
}
 
 
512
 
 
 
513
.embededmedia { margin-top: 5px }
 
 
514
 
501
.title { color: blue; padding: 0px; overflow: hidden; }
515
.title { color: blue; padding: 0px; overflow: hidden; }
502
.title:visited { color: #551a8b }
516
.title:visited { color: #551a8b }
503
.title.click { color: #551a8b } 
517
.title.click { color: #551a8b } 
...
 
...
 
1213
#passform.pretty-form button { padding: 0px 1px; }
1227
#passform.pretty-form button { padding: 0px 1px; }
1214
 
1228
 
1215
 
1229
 
1216
.prefleft { padding: 10px; font-weight: bold; vertical-align: top} 
1230
.preftable th { 
1217
.prefright { padding: 10px }
1231
    padding: 10px; 
 
 
1232
    font-weight: bold; 
 
 
1233
    vertical-align: top;
 
 
1234
    text-align: left;
 
 
1235
}
 
 
1236
.preftable td.prefright { padding: 10px }
 
 
1237
.preftable .spacer { margin-top: 5px; margin-bottom: 5px; }
1218
 
1238
 
1219
.over18 button { margin: 0 10px 0 10px; padding: 5px}
1239
.over18 button { margin: 0 10px 0 10px; padding: 5px}
1220
 
1240
 
...
 
...
 
1237
            color: #369; font-weight: bold;}
1257
            color: #369; font-weight: bold;}
1238
.stats td.ri { padding-left: 20px; text-align: right}
1258
.stats td.ri { padding-left: 20px; text-align: right}
1239
 
1259
 
 
 
1260
.thumbnail { 
 
 
1261
    float: left; 
 
 
1262
    margin: 0px 5px; 
 
 
1263
    overflow: hidden;
 
 
1264
}
 
 
1265
 
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
222
                    if(field) {
222
                    if(field) {
223
                        for(var i in u) {
223
                        for(var i in u) {
224
                            if(typeof(u[i]) != "function" && u != 'name') {
224
                            if(typeof(u[i]) != "function" && u != 'name') {
225
                                field[i] = u[i];
225
                                field[i] = unsafe(u[i]);
226
                            }
226
                            }
227
                        } }});
227
                        } }});
228
        my_iter(r.hide,
228
        my_iter(r.hide,
...
 
...
 
343
function new_captcha() {
343
function new_captcha() {
344
    redditRequest("new_captcha"); 
344
    redditRequest("new_captcha"); 
345
}
345
}
 
 
346
 
 
 
347
function view_embeded_media(id, media_link) {
 
 
348
    var eid = "embeded_media_" + id;
 
 
349
    var watchid = "view_embeded_media_span_watch_" + id;    
 
 
350
    var closeid = "view_embeded_media_span_close_" + id;
 
 
351
    var watchspan = document.getElementById(watchid);
 
 
352
    var closespan = document.getElementById(closeid);
 
 
353
    var e = document.getElementById(eid);
 
 
354
    if (e.style.display == "none") {
 
 
355
    e.style.display = "block";
 
 
356
    e.innerHTML = media_link;
 
 
357
    watchspan.style.display = "none";
 
 
358
    closespan.style.display = "inline";
 
 
359
    } else {
 
 
360
    e.style.display = "none";
 
 
361
    watchspan.style.display = "inline";
 
 
362
    closespan.style.display = "none";
 
 
363
    }
 
 
364
 
 
 
365
}
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%namespace file="utils.html" import="error_field"/>
23
<%namespace file="utils.html" import="error_field"/>
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%!
23
<%!
...
 
...
 
67
    %if thing.site:
67
    %if thing.site:
68
      <input type="hidden" name="sr" value="${thing.site._fullname}"/>
68
      <input type="hidden" name="sr" value="${thing.site._fullname}"/>
69
    %endif
69
    %endif
70
    <table>
70
    <table class="content preftable">
71
      <tr>
71
      <tr>
72
        <th>
72
        <th>
73
          <label for="name">${_("name")}</label>
73
          <label for="name">${_("name")}</label>
...
 
...
 
220
          <label>${_("type")}</label>
220
          <label>${_("type")}</label>
221
        </th>
221
        </th>
222
        <td colspan="2">
222
        <td colspan="2">
223
           <table>
223
           <table class="spacer">
224
           ${radio_type(_("public"), _("anyone can view and submit"))}
224
           ${radio_type(_("public"), _("anyone can view and submit"))}
225
           ${radio_type(_("restricted"), _("anyone can view, but only contributors can submit links"))}
225
           ${radio_type(_("restricted"), _("anyone can view, but only contributors can submit links"))}
226
           ${radio_type(_("private"), _("only contributors can view and submit"))}
226
           ${radio_type(_("private"), _("only contributors can view and submit"))}
...
 
...
 
237
          <label for="over_18">${_("viewers must be over eighteen years old")}</label>
237
          <label for="over_18">${_("viewers must be over eighteen years old")}</label>
238
        </td>
238
        </td>
239
      </tr>
239
      </tr>
 
 
240
      <tr>
 
 
241
        <th><label>${_("media")}</label></th>
 
 
242
        <td colspan="2">
 
 
243
          <input class="nomargin" type="checkbox" 
 
 
244
                 name="show_media" id="show_media"
 
 
245
                 ${thing.site and thing.site.show_media and "checked='checked'" or ""}/>
 
 
246
          <label for="show_media">
 
 
247
            ${_("show thumbnail images of content")}
 
 
248
          </label>
 
 
249
        </td>
 
 
250
      </tr>
240
 
251
 
241
      <tr>
252
      <tr>
242
        <th>
253
        <th>
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
1
## "The contents of this file are subject to the Common Public Attribution
1
## The contents of this file are subject to the Common Public Attribution
2
## License Version 1.0. (the "License"); you may not use this file except in
2
## License Version 1.0. (the "License"); you may not use this file except in
3
## compliance with the License. You may obtain a copy of the License at
3
## compliance with the License. You may obtain a copy of the License at
4
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
4
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
...
 
...
 
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved.
21
################################################################################
21
################################################################################
22
 
22
 
23
<%! from r2.models.subreddit import Default %>
23
<%!
 
 
24
   from r2.models.subreddit import Default 
 
 
25
 %>
24
 
26
 
25
<%inherit  file="printable.html"/>
27
<%inherit  file="printable.html"/>
26
 
28
 
...
 
...
 
39
  </span>
41
  </span>
40
</%def>
42
</%def>
41
 
43
 
42
<%def name="entry()">
44
<%def name="make_link(name, css_class)">
43
<% fullname = thing._fullname %>
45
  <a id="${name}_${thing._fullname}" 
44
<p class="title" id="titlerow_${fullname}">
46
     onmousedown="setClick(this, '${css_class}')"
45
  <a id="title_${fullname}" 
47
     class="${css_class} ${ c.user_is_loggedin and 'loggedin' or ''} ${thing.clicked and 'click' or ''}" 
46
     onmousedown="setClick(this)"
 
 
47
     class="title ${ c.user_is_loggedin and 'loggedin' or ''} ${thing.clicked and 'click' or ''}" 
 
 
48
     %if c.user.pref_frame:
48
     %if c.user.pref_frame:
49
       href="/goto?id=${thing._id36}"  
49
       href="/goto?id=${thing._id36}"  
50
     %else:
50
     %else:
...
 
...
 
57
       target="_blank" 
57
       target="_blank" 
58
     %endif
58
     %endif
59
     >
59
     >
60
        ${thing.title} 
60
     ${caller.body()}
61
     </a>
61
  </a>
 
 
62
</%def>
 
 
63
 
 
 
64
<%def name="entry()">
 
 
65
  <p class="title" id="titlerow_${thing._fullname}">
 
 
66
    <%call expr="make_link('title', 'title')">
 
 
67
      ${thing.title} 
 
 
68
    </%call>
62
    &#32;
69
    &#32;
63
    ${unsafe(self.domain())}
70
    ${unsafe(self.domain())}
64
</p>
71
  </p>
65
<p class="tagline">
72
  <p class="tagline">
66
  ${self.tagline()}
73
    ${self.tagline()}
67
</p>
74
  </p>
68
<ul class="flat-list buttons">
75
  <ul class="flat-list buttons">
69
  ${self.buttons()}
76
    ${self.buttons()}
70
  ${self.admintagline()}
77
    ${self.admintagline()}
71
</ul>
78
  </ul>
 
 
79
  ${self.mediadiv()}
72
</%def>
80
</%def>
73
 
81
 
74
<%def name="subreddit()" buffered="True">
82
<%def name="subreddit()" buffered="True">
...
 
...
 
92
    %endif                  
100
    %endif                  
93
    ${self.arrow(thing, 0, thing.likes == False)}
101
    ${self.arrow(thing, 0, thing.likes == False)}
94
  </div>
102
  </div>
 
 
103
 ${self.thumbnail()}
95
</%def>
104
</%def>
96
 
105
 
97
 
106
 
...
 
...
 
161
  %endif
170
  %endif
162
  ${parent.delete_or_report_buttons()}
171
  ${parent.delete_or_report_buttons()}
163
  ${parent.buttons()}
172
  ${parent.buttons()}
 
 
173
  ${self.media_embed()}
 
 
174
</%def>
 
 
175
 
 
 
176
 
 
 
177
<%def name="media_embed()">
 
 
178
  %if thing.media_object:
 
 
179
  <li>
 
 
180
    <a id="view_embeded_media_a_${thing._fullname}" class="" \
 
 
181
       href="javascript:view_embeded_media('${thing._fullname}', '${thing.media_object}')">\
 
 
182
      <span id="view_embeded_media_span_watch_${thing._fullname}" 
 
 
183
            class="watch-play"
 
 
184
            style="display: inline">
 
 
185
        ${_("watch")}
 
 
186
      </span>
 
 
187
      <span id="view_embeded_media_span_close_${thing._fullname}" 
 
 
188
            class="watch-stop"
 
 
189
            style="display: none">
 
 
190
        ${_("close")}
 
 
191
      </span>
 
 
192
    </a>
 
 
193
  </li>
 
 
194
  %endif
164
</%def>
195
</%def>
165
 
196
 
 
 
197
<%def name="thumbnail()">
 
 
198
  %if thing.thumbnail:
 
 
199
  <%call expr="make_link('thumbnail', 'thumbnail')">
 
 
200
    <img src="${thing.thumbnail}" alt="thumbnail for ${thing._fullname}"/>
 
 
201
  </%call>
 
 
202
  %endif
 
 
203
</%def>
166
 
204
 
 
 
205
<%def name="mediadiv()">
 
 
206
  %if thing.media_object:
 
 
207
    <div id="embeded_media_${thing._fullname}" 
 
 
208
         class="embededmedia" style="display: none;">
 
 
209
      <p class="error">loading...</p>
 
 
210
    </div>
 
 
211
  %endif
 
 
212
</%def>
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%!
23
<%!
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%namespace file="help.html" import="help_or_hide"/>
23
<%namespace file="help.html" import="help_or_hide"/>
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%namespace file="utils.html" import="language_tool, language_checkboxes"/>
23
<%namespace file="utils.html" import="language_tool, language_checkboxes"/>
...
 
...
 
34
<%def name="link_options()">
34
<%def name="link_options()">
35
  <select name="numsites" style="margin: 0 .5em 0 .5em">
35
  <select name="numsites" style="margin: 0 .5em 0 .5em">
36
        %for x in [10, 25, 50, 100]:
36
        %for x in [10, 25, 50, 100]:
37
        <option ${x == c.user.pref_numsites and "selected='selected'" or ""}>${x}</option>
37
        <option ${x == c.user.pref_numsites and "selected='selected'" or ""}>
 
 
38
          ${x}
 
 
39
        </option>
38
        %endfor
40
        %endfor
39
  </select>
41
  </select>
40
</%def>
42
</%def>
41
 
43
 
 
 
44
<%def name="media_radio(val, label)">
 
 
45
  <input id="media_${val}" class="nomargin" 
 
 
46
         type="radio"  value="${val}" name="media"
 
 
47
         ${"checked='checked'" if c.user.pref_media == val else ''} /> 
 
 
48
  <label for="media_${val}">${label}</label>
 
 
49
  <br/>
 
 
50
</%def>
42
 
51
 
43
<%def name="num_input(s, name)">
52
<%def name="num_input(s, name)">
44
  <input type="text" size="4" maxlength="4" 
53
  <input type="text" size="4" maxlength="4" 
...
 
...
 
54
<input type="hidden" name="uh" value="${c.modhash}" />
63
<input type="hidden" name="uh" value="${c.modhash}" />
55
 
64
 
56
<table class="content preftable">
65
<table class="content preftable">
57
  <tr class="prefrow">
66
  <tr>
58
    <td class="prefleft">${_("interface language")}</td>
67
    <th>${_("interface language")}</th>
59
    <td class="prefright">
68
    <td class="prefright">
60
      ${language_tool(allow_blank = False, show_regions = True,
69
      ${language_tool(allow_blank = False, show_regions = True,
61
                      default_lang = c.user.pref_lang)}
70
                      default_lang = c.user.pref_lang)}
...
 
...
 
63
        &#32;<a href="/feedback">${_("volunteer to translate")}</a></span>
72
        &#32;<a href="/feedback">${_("volunteer to translate")}</a></span>
64
    </td>
73
    </td>
65
  </tr>
74
  </tr>
66
  <tr class="prefrow">
75
  <tr>
67
    <td class="prefleft">${_("content language")}</td>
76
    <th>${_("content language")}</th>
68
    <td class="prefright">
77
    <td class="prefright">
69
      ${language_checkboxes(default = c.user.pref_content_langs)}
78
      ${language_checkboxes(default = c.user.pref_content_langs)}
70
    </td>
79
    </td>
71
  </tr>
80
  </tr>
72
  %if c.user_is_loggedin:
81
  %if c.user_is_loggedin:
73
  <tr class="prefrow">
82
  <tr>
74
    <td class="prefleft">${_("clicking options")}</td>
83
    <th>${_("clicking options")}</th>
75
    <td class="prefright">
84
    <td class="prefright">
76
      ${checkbox(_("display links with a reddit toolbar"), "frame")}
85
      ${checkbox(_("display links with a reddit toolbar"), "frame")}
77
      <br/>
86
      <br/>
78
      ${checkbox(_("open links in a new window"), "newwindow")}
87
      ${checkbox(_("open links in a new window"), "newwindow")}
79
    </td>
88
    </td>
80
  </tr>
89
  </tr>
81
  <tr class="prefrow">
90
  <tr>
82
    <td class="prefleft">${_("link options")}</td>
91
    <th>${_("media")}</th>
 
 
92
    <td class="prefright">
 
 
93
      %if not c.user.pref_compress:
 
 
94
      ${media_radio("on", _("show thumbnails next to links"))}
 
 
95
      ${media_radio("off", _("don't show thumbnails next to links"))}
 
 
96
      ${media_radio("subreddit", _("show thumbnails based on that reddit's media preferences"))}
 
 
97
      %else:
 
 
98
      <p class="error">${_("to enable thumbnails, disable compressed link display")}</p>
 
 
99
      <input type="hidden" name="media" value="${c.user.pref_media}"/>
 
 
100
      %endif
 
 
101
    </td>
 
 
102
  </tr>
 
 
103
  <tr>
 
 
104
    <th>${_("link options")}</th>
83
    <td class="prefright">
105
    <td class="prefright">
84
      ${checkbox(_("show me new links on the front page"), "organic")}
106
      ${checkbox(_("show me new links on the front page"), "organic")}
85
      <br/>
107
      <br/>
...
 
...
 
108
      &#32;<span class="little gray">${_("(blank for none)")}</span>
130
      &#32;<span class="little gray">${_("(blank for none)")}</span>
109
    </td>
131
    </td>
110
  </tr>
132
  </tr>
111
  <tr class="prefrow">
133
  <tr>
112
    <td class="prefleft">${_("comment options")}</td>
134
    <th>${_("comment options")}</th>
113
    <td class="prefright">
135
    <td class="prefright">
114
    <% 
136
    <% 
115
       input = capture(num_input, c.user.pref_min_comment_score,
137
       input = capture(num_input, c.user.pref_min_comment_score,
...
 
...
 
129
    </td>
151
    </td>
130
  </tr>
152
  </tr>
131
 
153
 
132
  <tr class="prefrow">
154
  <tr>
133
    <td class="prefleft">${_("privacy options")}</td>
155
    <th>${_("privacy options")}</th>
134
    <td class="prefright">
156
    <td class="prefright">
135
      ${checkbox(_("make my votes public"), "public_votes")}
157
      ${checkbox(_("make my votes public"), "public_votes")}
136
      <br/>
158
      <br/>
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%! 
23
<%! 
...
 
...
 
173
  </div>
173
  </div>
174
</%def>
174
</%def>
175
 
175
 
176
 
 
 
177
 
 
 
178
<%def name="score(this, likes=None, inline=True, label = True, _id = True)">
176
<%def name="score(this, likes=None, inline=True, label = True, _id = True)">
179
<%
177
<%
180
   tag = "span" if inline else "div"
178
   tag = "span" if inline else "div"
...
 
...
 
254
  ${title}</a>
252
  ${title}</a>
255
</%def>
253
</%def>
256
 
254
 
 
 
255
<%def name="advanced_button(fullname, args, title, nameFunc=None)">
 
 
256
<a id="${nameFunc}_a_${fullname}" class="" \
 
 
257
   href="javascript:${nameFunc}(${args})">\
 
 
258
  ${title}</a>
 
 
259
</%def>
 
 
260
 
257
<%def name="tags(**kw)">
261
<%def name="tags(**kw)">
258
%for k, v in kw.iteritems():
262
%for k, v in kw.iteritems():
259
%if v is not None:
263
%if v is not None:
7bf5fab1fa2fa07cc012c0570781aea23343ae05bcca862eaaeeb4e5a739a9107f8ae073ef6a37b3
1
## "The contents of this file are subject to the Common Public Attribution
1
## "The contents of this file are subject to the Common Public Attribution.
2
## License Version 1.0. (the "License"); you may not use this file except in
2
## License Version 1.0. (the "License"); you may not use this file except in
3
## compliance with the License. You may obtain a copy of the License at
3
## compliance with the License. You may obtain a copy of the License at
4
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
4
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
...
 
...
 
17
## the Original Code is CondeNet, Inc.
17
## the Original Code is CondeNet, Inc.
18
## 
18
## 
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
19
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
20
## CondeNet, Inc. All Rights Reserved.
20
## CondeNet, Inc. All Rights Reserved."
21
################################################################################
21
################################################################################
22
 
22
 
23
<%!
23
<%!