LJSUP-17669: Login.bml form refactoring
[livejournal.git] / cgi-bin / LJ / PageStats.pm
blob94659476d8aaa8e1f5fc5b1e2d8f8108a57a8b3a
1 package LJ::PageStats;
2 use strict;
3 use LJ::Request;
4 use Digest::MD5 qw/md5_base64/;
6 # loads a page stat tracker
7 sub new {
8 my ($class) = @_;
10 my $self = {
11 conf => \%LJ::PAGESTATS_PLUGIN_CONF,
12 ctx => '',
15 bless $self, $class;
16 return $self;
19 # render JS output for embedding in pages
20 # ctx can be "journal" or "app". defaults to "app".
21 sub render {
23 my ($self, $params) = @_;
25 my $ctx = $self->get_context;
27 return '' unless $self->should_do_pagestats;
29 my $output = '';
30 foreach my $plugin ($self->get_active_plugins) {
31 my $class = "LJ::PageStats::$plugin";
32 eval "use $class; 1;";
33 die "Error loading PageStats '$plugin': $@" if $@;
34 my $plugin_obj = $class->new;
35 next unless $plugin_obj->should_render;
36 $output .= $plugin_obj->render(conf => $self->{conf}->{$plugin}, params => (ref($params) eq 'HASH' ? $params : {}) );
39 # return nothing
40 return "<div id='hello-world' style='text-align: left; font-size:0; line-height:0; height:0; overflow:hidden;'>$output</div>";
43 # method on root object (LJ::PageStats instance) to decide if user has optted-out of page
44 # stats tracking, or if it's a bad idea to show one to this user (underage). but
45 # this isn't pagestat-specific logic. that's in the "should_render" method.
46 sub should_do_pagestats {
47 my $self = shift;
49 my $u = $self->get_user;
51 # Make sure the user isn't underage or said no to tracking
52 return 0 if $u && $u->underage;
53 return 0 if $u && $u->prop('opt_exclude_stats');
54 return 1;
57 # decide if tracker should be embedded in page
58 sub should_render {
59 my ($self) = @_;
61 my $ctx = $self->get_context;
62 return 0 unless ($ctx && $ctx =~ /^(app|journal)$/);
64 LJ::Request->is_inited or return 0;
66 # Make sure we don't exclude tracking from this page or path
67 return 0 if grep { LJ::Request->uri =~ /$_/ } @{ $LJ::PAGESTATS_EXCLUDE{'uripath'} };
68 return 0 if grep { LJ::Request->notes('codepath') eq $_ } @{ $LJ::PAGESTATS_EXCLUDE{'codepath'} };
70 # Turned off. AMyshkin
71 # See if their ljuniq cookie has the PageStats flag
72 # if ($BML::COOKIE{'ljuniq'} =~ /[a-zA-Z0-9]{15}:\d+:pgstats([01])/) {
73 # return 0 unless $1; # Don't serve PageStats if it is "pgstats:0"
74 # } else {
75 # return 0; # They don't have it set this request, but will for the next one
76 # }
78 return 1;
81 sub get_context {
82 my ($self) = @_;
84 return $self->get_journal() ? 'journal' : 'app';
87 sub get_user {
88 my ($self) = @_;
90 return LJ::get_remote();
93 # return Apache request
94 sub get_request {
95 my ($self) = @_;
97 return LJ::Request->r;
100 sub get_root {
101 my ($self) = @_;
103 return $LJ::IS_SSL ? $LJ::SSLROOT : $LJ::SITEROOT ;
106 sub get_active_plugins {
107 my ($self) = @_;
109 my $conf = $self->get_conf;
111 return () unless $conf;
113 return @{$conf->{_active} || []};
116 sub get_conf {
117 my ($self) = @_;
119 return $self->{conf};
122 sub get_plugin_conf {
123 my ($self) = @_;
125 my $plugin = ref $self; $plugin =~ s/^LJ::PageStats:://;
127 return $LJ::PAGESTATS_PLUGIN_CONF{$plugin};
130 sub filename {
131 my ($self) = @_;
133 my $filename = LJ::Request->uri;
135 return $filename;
138 sub codepath {
139 my ($self) = @_;
141 my $codepath = LJ::Request->notes('codepath');
142 # remove 's2.' or 's1.' prefix from codepath
143 $codepath =~ s/^[Ss]\d{1}\.(.*)$/$1/;
145 # map some s1 codepath names to s2
146 my %s1_map = (
147 'bml.talkpost' => "reply",
148 'bml.talkread' => "entry",
149 'bml.view.index' => "calendar",
152 foreach my $s1code (keys %s1_map) {
153 $codepath = $s1_map{$s1code} if ($codepath =~ /^$s1code$/);
156 return $codepath;
159 sub pagename {
160 my ($self) = @_;
162 my $pagename = '';
164 if ($self->is_journal_ctx) {
165 $pagename = $self->codepath;
166 } else {
167 $pagename = $self->filename;
170 return $pagename;
173 sub get_journal {
174 my $self = shift;
176 my $j = LJ::get_active_journal();
177 return $j if $j;
179 # Now try to determine active_journal from base request if it is requests chain.
180 # Cache it in $self->{active_journal}.
181 # This code is necessary for getting active_journal in 'error-page.bml'.
183 return $self->{active_journal} if exists $self->{active_journal};
185 $self->{active_journal} = undef;
187 if (!LJ::Request->is_initial_req())
189 my $request = LJ::Request->prev();
190 my $host = $request->header_in('Host');
191 my $uri = $request->uri;
193 if (($LJ::USER_VHOSTS || $LJ::ONLY_USER_VHOSTS) &&
194 $host =~ /^([\w\-]{1,15})\.\Q$LJ::USER_DOMAIN\E$/ &&
195 $1 ne 'www')
197 my $user = $1;
199 my $func = $LJ::SUBDOMAIN_FUNCTION{$user};
201 if ($func eq 'journal' && $uri =~ m!^/(\w{1,15})(/.*)?$!) {
202 $user = $1;
204 elsif ($func) {
205 $user = '';
208 if ($user) {
209 my $u = LJ::load_user($user);
210 $self->{active_journal} = $u if $u;
215 return $self->{active_journal};
218 sub journaltype {
219 my $self = shift;
221 my $j = $self->get_journal;
223 return $j->journaltype_readable;
226 sub journalbase {
227 my $self = shift;
229 my $j = $self->get_journal;
231 return $j->journal_base;
234 sub is_journal_ctx {
235 my $self = shift;
236 my $ctx = $self->get_context;
238 return 1 if ($ctx eq 'journal');
239 return 0;
242 # not implemented for livejournal
243 sub groups {
244 my ($self) = @_;
246 return undef;
249 sub scheme {
250 my ($self) = @_;
252 my $scheme = BML::get_scheme();
253 $scheme = (LJ::site_schemes())[0]->{'scheme'} unless $scheme;
255 return $scheme;
258 sub language {
259 my ($self) = @_;
261 my $lang = LJ::Lang::get_effective_lang();
263 return $lang;
266 sub loggedin {
267 my ($self) = @_;
269 my $loggedin = $self->get_user ? '1' : '0';
271 return $loggedin;
274 sub campaign_tracking {
275 my ($self, $opts) = @_;
277 return '' unless $self->should_do_pagestats;
279 my $output = '';
280 foreach my $plugin ($self->get_active_plugins) {
281 my $class = "LJ::PageStats::$plugin";
282 eval "use $class; 1;";
283 die "Error loading PageStats '$plugin': $@" if $@;
284 my $plugin_obj = $class->new;
285 next unless $plugin_obj->should_render;
286 next unless ($plugin_obj->can('campaign_track_html'));
287 $output .= $plugin_obj->campaign_track_html($opts);
290 return $output;
293 sub account_level {
294 my ($self, $u) = @_;
295 my $level;
297 if ($u) {
298 if ($u->identity) {
299 $level = 'plus';
300 } else {
301 if (LJ::get_cap($u, 'paid')) {
302 if ($u->in_class('perm')) {
303 $level = 'perm';
304 } elsif ($u->in_class('sponsored')) {
305 $level = 'sponsored';
306 } else {
307 $level = 'paid';
309 } elsif ($u->in_class('plus')) {
310 $level = 'plus';
311 } else {
312 $level = 'basic';
317 return $level;
320 sub style_system {
321 my ($self, $journal) = @_;
323 return 'undef' unless $journal;
324 return LJ::Request->notes('codepath') =~ m/^([sS]\d)\./ ? lc($1) : 'undef';
327 sub style_layout {
328 my ($self, $journal) = @_;
330 return 'undef' unless $journal;
332 my $style_system = $self->style_system($journal);
333 my $style_layout;
334 if ($style_system eq 's1') {
335 $style_layout = $journal->{'_s1styleid'} && LJ::S1::get_style($journal->{'_s1styleid'})->{'styledes'} || 'own_style';
336 } elsif ($style_system eq 's2') {
337 $style_layout = 'own_style';
338 if ($journal->{'_s2styleid'}) {
339 my %style = LJ::S2::get_style($journal->{'_s2styleid'});
340 $style_layout = defined $style{'layout'} && S2::get_layer_info($style{'layout'}, 'name');
341 unless ($style_layout) {
342 LJ::S2::load_layers($style{'layout'});
343 $style_layout = S2::get_layer_info($style{'layout'}, 'name') || 'own_style';
344 S2::unregister_layer($style{'layout'});
346 } elsif (defined $journal->{'_s2styleid'}) {
347 $style_layout = 'default';
349 } else {
350 $style_layout = 'undef';
353 return $style_layout;
356 sub comments_style {
357 my ($self, $journal) = @_;
359 return 'undef' unless $journal;
361 my $remote = LJ::get_remote();
362 my $style = LJ::Request->get_param('style');
363 my $format = LJ::Request->get_param('format') || '';
364 my $stylemine = ($style && $style eq 'mine') ? 1 : 0;
365 my $style_u = $journal;
367 my $comments_style = 's1';
369 my ($stylesys, $styleid);
371 if ($remote && ($stylemine || $remote->opt_stylealwaysmine || $remote->opt_commentsstylemine)) {
372 $style_u = $remote;
375 LJ::load_user_props($journal, ("stylesys", "s2_style"));
377 my $forceflag = 0;
379 LJ::run_hooks("force_s1", $journal, \$forceflag);
381 if ( not $forceflag and $journal->{'stylesys'} and $journal->{'stylesys'} == 2 ) {
382 $stylesys = 2;
383 $styleid = $journal->{'s2_style'};
384 } else {
385 $stylesys = 1;
386 $styleid = 0;
389 if ( $stylesys == 2 ) {
390 my $style = LJ::Customize->verify_and_load_style ($journal);
391 my $prop_value = undef;
392 unless ($style) {
393 my $ctx = LJ::S2::s2_context('UNUSED', $styleid);
394 $LJ::S2::CURR_CTX = $ctx;
395 $prop_value = $ctx->[S2::PROPS]->{'view_entry_disabled'};
396 warn "Style probably corrupted. User: " . $journal->username;
397 } else {
398 $prop_value = LJ::MemCache::get_or_set (
399 [$journal->userid, "s2prop:".$journal->userid.":view_entry_disabled"],
400 sub { return LJ::Customize->get_s2_prop_values ("view_entry_disabled", $journal, $style); },
401 3600
405 $comments_style = 's2'
406 if (not $prop_value and LJ::get_cap($journal, "s2viewentry")) || $LJ::JOURNALS_WITH_FIXED_STYLE{$journal->user};
409 if ( $format eq 'light' ) {
410 $comments_style = 's1';
413 return $comments_style;
416 sub is_homepage {
417 return LJ::Request->current_page_url() =~ m{^$LJ::SITEROOT(?:/welcome/?|/latest/?|/editors/?|/category/\w+/?)?/?$} ? 1 : 0;
420 sub homepage_category {
421 my ($self) = @_;
423 my $url = LJ::Request->current_page_url();
425 if (my ($cat_pretty_name) = $url =~ m{^$LJ::SITEROOT/category/(\w+)/?$}) {
426 for (@{LJ::HomePage::Category->get_all_categories}) {
427 return $cat_pretty_name
428 if $cat_pretty_name eq $_->{'pretty_name'};
432 return 'undef';
435 sub homepage_flags {
436 my ($self) = @_;
438 my $remote = LJ::get_remote();
440 return {
441 geotargeting => LJ::PersonalStats::Ratings->get_rating_country() || 'undef',
442 unique_items => LJ::User::HomePage->homepage_flag ($remote, 'show_unique_items') ? 'show' : 'hide',
443 from_friends => LJ::User::HomePage->homepage_flag ($remote, 'show_from_friends') ? 'show' : 'hide',
444 hidden_items => LJ::User::HomePage->homepage_flag ($remote, 'show_hidden_items') ? 'show' : 'hide',
448 sub user_params {
449 my ($self, $u) = @_;
451 my $req = !LJ::Request->is_initial_req && LJ::Request->prev || LJ::Request->request;
453 my $host = $req->header_in('Host');
454 my $uri = $req->uri;
455 my $args = $req->args;
457 $args = "?$args" if $args;
459 # Special requirement from ATI:
460 # The character „&“ should not be used, or encoded two times. O_O
461 $args =~ s/(&)/LJ::eurl($1)/eg;
463 my $url = LJ::eurl("$host$uri$args");
465 if ( $u ) {
467 my $journaltype = $u->journaltype_readable;
468 my $journal_user = $u->user;
470 if ($journaltype eq 'redirect' && (my $renamedto = LJ::load_user($u->prop('renamedto')))) {
471 $journaltype = $renamedto->journaltype_readable;
474 return {
475 userid => $u->userid(),
476 stylealwaysmine => $u->opt_stylealwaysmine ? 'yes' : 'no',
477 login_service => $u->identity ? $u->identity->short_code : 'lj',
478 sup_enabled => LJ::SUP->is_sup_enabled($u) ? 'Cyr' : 'nonCyr',
479 premium_package => $u->get_cap('perm') ? 'perm' : $u->get_cap('paid') ? 'paid' : 'no',
480 account_level => $self->account_level($u),
481 page_params => "journal::$journaltype\:\:$journal_user\:\:$url",
482 adult_content => $u->adult_content_calculated,
483 early_adopter => LJ::get_cap($u, 'early') ? 'yes' : 'no',
484 user_md5_base64 => md5_base64($u->user, 0, 8),
485 user => $journal_user,
486 journaltype => $journaltype,
489 } else {
491 my $ip_class = LJ::GeoLocation->ip_class();
493 return {
494 userid => '',
495 stylealwaysmine => 'undef',
496 login_service => 'undef',
497 sup_enabled => LJ::SUP->is_sup_ip_class($ip_class) ? 'Cyr' : 'nonCyr',
498 premium_package => 'undef',
499 account_level => 'undef',
500 page_params => "service::undef::undef::$url",
501 adult_content => 'undef',
502 early_adopter => 'undef',
503 user_md5_base64 => 'undef',
504 user => 'undef',
505 journaltype => 'undef',