1 package LJ
::TopEntries
;
4 use base
qw(LJ::Widget);
8 use LJ
::RelationService
;
13 · Computers
& Technology
28 · Spirituality
& Beliefs
32 hmp_ontd
=> 'Homepage/ONTD',
33 hmp_spotlight
=> 'Homepage/Spotlight',
34 anythingdisney
=> 'AnythingDiz',
35 bullying_begone
=> 'bullying_begone',
36 map { ($_ => $_) } qw{
39 craftgrrlontd
-political
52 culture
=> 'Arts & Culture',
53 entertainment
=> 'Entertainment',
54 books
=> 'Books & Writing',
55 computers
=> 'Computers & Technology',
56 fashion
=> 'Fashion & Style',
57 food
=> 'Food & Drink',
59 health
=> 'Health & Fitness',
61 lj_culture
=> 'LiveJournal Culture',
62 news
=> 'News & Politics',
63 regional
=> 'Regional',
64 schools
=> 'Schools & Education',
65 science
=> 'Science & Nature',
66 society
=> 'Society & Culture',
67 spirit
=> 'Spirituality & Beliefs',
71 # if domain key is equal to community name, than no need to insert this domain here
72 my %community_for_domain = (
73 hmp_ontd
=> 'ohnotheydidnt'
82 craftgrrlontd
-political
113 sub domain2name
{ my $class = shift; $domains{ +shift } }
119 return @order if LJ
::check_priv
($u, "siteadmin", "topentries");
123 foreach my $candidate (@order) {
124 my $comm_name = $community_for_domain{$candidate};
125 $comm_name = $candidate unless $comm_name;
126 my $comm = LJ
::load_user
($comm_name);
127 my $allow_access = LJ
::check_rel
($comm, $u, 'S') ||
128 LJ
::check_rel
($comm, $u, 'A');
130 next unless $allow_access;
133 push @result, $candidate if $u and $u->can_manage($comm);
142 my $domain = $opts{domain
};
144 ## prev API to this class
145 ## it can be removed after #66 release
146 $domain = 'hmp_ontd';
148 Carp
::confess
("unknown domain: $domain")
149 unless exists $domains{$domain};
151 my $self = {domain
=> $domain};
152 $self->{remote
} = $opts{remote
} || LJ
::get_remote
();
153 $self->{timelimit
} = $opts{timelimit
} || 24 * 3600;
155 return bless $self, $class;
158 # key <---> hash. Key - a string with four numbers, hash - full info about post.
162 return "$h->{'timestamp'}:$h->{'journalid'}:$h->{'jitemid'}:$h->{'userpicid'}";
169 my $timestamp = $args{timestamp
};
170 my $journalid = $args{journalid
};
171 my $jitemid = $args{jitemid
};
172 my $userpicid = $args{userpicid
};
173 my $tags = $args{tags
};
174 my $vertical_name = $args{vertical_name
};
175 my $vertical_uri = $args{vertical_uri
};
178 # my ($timestamp, $journalid, $jitemid, $userpicid) = @$key;
180 return undef unless $journalid && $jitemid && $userpicid;
182 my $entry = LJ
::Entry
->new($journalid, jitemid
=> $jitemid);
184 return undef unless $entry;
186 my $poster = $entry->poster();
187 my $journal = LJ
::load_userid
($journalid);
189 return undef unless $poster && $journal;
191 # Get userpic from entry
192 my $userpic = LJ
::Userpic
->new($poster, $userpicid);
194 return undef unless $userpic && $userpic->valid();
196 my $subject = $entry->subject_text();
197 my $subject_trimed = LJ
::html_trim_4gadgets
($subject, 70, '');
198 $subject_trimed .= '...' if $subject_trimed ne $subject;
202 posterid
=> $poster->{'userid'},
203 journalid
=> $journalid,
205 userpicid
=> $userpicid,
207 subj
=> $subject_trimed,
208 text
=> LJ
::html_trim_4gadgets
($entry->event_text(), 50, $entry->url()),
209 #revtime => $entry->prop('revtime'),
210 url
=> $entry->url(),
211 time => $entry->logtime_unix(),
212 userpic
=> $userpic->url(),
214 ## Do not store results of ljuser_display() anywhere
215 ## becouse they depend on $remote user.
216 ## this field should be generated on fly.
217 # poster => $poster->ljuser_display(),
219 timestamp
=> $timestamp,
221 comments
=> $entry->reply_count,
222 comments_url
=> $entry->url(anchor
=> 'comments'),
224 logtime
=> $entry->logtime_unix,
226 vertical_name
=> $vertical_name,
227 vertical_uri
=> $vertical_uri,
229 key
=> "$journalid:$jitemid",
233 # Clean list before store: remove old elements.
238 my @list = sort {$b->{'timestamp'} <=> $a->{'timestamp'}} @
{$self->{'featured_posts'}};
240 return @list if $self->{'min_entries'} >= scalar @list; # We already has a minimum.
242 # Remove old entries - stay at least 'min_entries' recent and all within 24h from now.
243 my $time_edge = time() - $self->{'timelimit'};
244 my $count = $self->{'min_entries'};
245 @list = grep { ($count-- > 0) || ($time_edge - $_->{'timestamp'} < 0) } @list;
254 sort {$b->{'timestamp'} <=> $a->{'timestamp'}}
255 grep { $_ && !($_->{'revtime'} && $_->{'revtime'} > $_->{'timestamp'}) } # Sanity check
256 @
{$self->{'featured_posts'}};
258 return @list if $opts{'raw'};
260 # Remove old entries - stay at least 'min_entries' recent and all within 24h from now.
261 my $time_edge = time() - 24 * 3600;
262 my $count = $self->{'min_entries'};
263 @list = grep { ($count-- > 0) || ($time_edge - $_->{'timestamp'} < 0) } @list;
265 # Remove elements below 'max_entries'.
266 $count = scalar @list - $self->{'max_entries'};
268 return @list if $count <= 0;
271 splice @list, int(rand(scalar @list)), 1;
277 # store all from blessed hash to journal property.
278 sub _store_featured_posts
{
282 # my $prop = $self->{'min_entries'} . ':' . $self->{'max_entries'} . ':0:0|' .
283 # join('|', map { $self->_key_from_hash($_) } $self->_clean_list(%opts));
287 my @spots = $self->_clean_list(%opts);
289 min_entries
=> $self->{min_entries
},
290 max_entries
=> $self->{max_entries
},
291 timelimit
=> $self->{timelimit
},
295 my $data = Storable
::nfreeze
($struct);
298 my $domain = $self->{domain
};
299 LJ
::ExtBlock
->create_or_replace("spts_$domain" => $data);
301 $self->{_post_loaded
} = 0;
304 # load all from property to blessed hash.
305 sub _load_featured_posts
{
309 return if $self->{_post_loaded
};
311 my $domain = $self->{domain
};
312 my $ext_block = LJ
::ExtBlock
->load_by_id("spts_$domain", {skip_local_cache
=> 1});
313 my $prop_val = $ext_block ?
$ext_block->blocktext : '';
315 $prop_val = '3:5:0:0' unless $prop_val;
317 my @entities = map { [ split /:/ ] } split(/\|/, $prop_val);
319 my ($min_entries, $max_entries, undef, undef) = @
{shift @entities};
321 $self->{'min_entries'} = $min_entries;
322 $self->{'max_entries'} = $max_entries;
323 $self->{'featured_posts'} = [ map { $self->_hash_from_key($_) } @entities ];
326 my $struct = eval { Storable
::thaw
($prop_val) };
327 $self->{min_entries
} = $struct->{min_entries
};
328 $self->{max_entries
} = $struct->{max_entries
};
329 $self->{featured_posts
} = $struct->{spots
} || [];
330 $self->{timelimit
} = $struct->{timelimit
};
333 $users = LJ
::load_userids
( map {$_->{posterid
} } @
{ $self->{featured_posts
} } )
334 if ref $self->{featured_posts
} eq 'ARRAY';
336 ## Update comments couter
337 foreach my $spot (@
{ $self->{featured_posts
} }){
338 my $entry = LJ
::Entry
->new($spot->{journalid
}, jitemid
=> $spot->{jitemid
});
341 my $u = $users->{$spot->{posterid
}};
344 $spot->{comments
} = $entry->reply_count();
345 $spot->{logtime
} = $spot->{time} = $entry->logtime_unix();
347 $spot->{poster
} = $u->ljuser_display();
350 $self->{min_entries
} ||= 3;
351 $self->{max_entries
} ||= 5;
352 $self->{timelimit
} ||= 24*3600;
354 $self->{_post_loaded
} = 1;
356 return $self->_sort_list(%opts);
360 sub get_featured_posts
{
363 return $self->{'featured_posts'} ?
364 $self->_sort_list(%opts) : $self->_load_featured_posts(%opts);
370 $self->_load_featured_posts();
372 my $min_entries = shift;
373 if ($self->{'min_entries'} != $min_entries) {
374 $self->{'min_entries'} = $min_entries;
375 $self->_store_featured_posts();
379 return $self->{'min_entries'};
385 $self->_load_featured_posts();
387 my $max_entries = shift;
388 if ($self->{'max_entries'} != $max_entries) {
389 $self->{'max_entries'} = $max_entries;
390 $self->_store_featured_posts();
394 return $self->{'max_entries'};
400 $self->_load_featured_posts();
403 if (my $hours = shift @_){
404 $self->{timelimit
} = $hours * 3600; ## keep in seconds
405 $self->_store_featured_posts();
407 return $self->{timelimit
} / 3600;
415 my $entry = $opts{'entry'};
416 return 'wrong entry' unless $entry;
418 my $tags = $opts{tags
};
419 my $vertical_name = $opts{vertical_name
};
420 my $vertical_uri = $opts{vertical_uri
};
422 my $timestamp = time();
424 my ($journalid, $jitemid, $poster, $userpic) =
425 ($entry->journalid(), $entry->jitemid(), $entry->poster(), $entry->userpic());
427 return 'wrong entry poster' unless $poster;
429 my $userpicid = $userpic ?
$userpic->id() : ($poster->{'defaultpicid'} || 0);
431 $self->delete_entry(key
=> "$journalid:$jitemid");
433 $self->get_featured_posts(raw
=> 1, %opts); # make sure we has all fresh data
435 ## Fullfill with other data
436 my $post = $self->_hash_from_key(
437 timestamp
=> $timestamp,
438 journalid
=> $journalid,
440 userpicid
=> $userpicid,
442 vertical_name
=> $vertical_name,
443 vertical_uri
=> $vertical_uri,
447 push @
{$self->{'featured_posts'}}, $post;
448 $self->_store_featured_posts(%opts);
452 # all other error conditions checked before call _hash_from_key()
453 return 'userpic missed or does not valid';
460 return unless $opts{'key'} =~ /(\d+):(\d+)/;
462 my ($journalid, $jitemid) = ($1, $2);
463 return unless $journalid && $jitemid;
465 @
{$self->{'featured_posts'}} = grep {
466 ! ( $_->{'journalid'} == $journalid && $_->{'jitemid'} == $jitemid )
467 } $self->get_featured_posts(raw
=> 1, %opts);
469 $self->_store_featured_posts(%opts);