# ================================================================== # Plugins::SocialMedia - Auto Generated Program Module # # Plugins::SocialMedia # Author : Gossamer Threads Inc. # Version : 1.0 # Updated : Tue Oct 29 11:32:05 2013 # # ================================================================== # package Plugins::SocialMedia; # ================================================================== use strict; use GT::Base; use GT::Plugins qw/STOP CONTINUE/; use Links qw/:objects/; use Links::Build; use Links::SiteHTML; use Net::Twitter; use Links::Plugins; use LWP::Simple; use JSON; use URI; use utf8; use vars qw/$USE_HTML $TIME_START $TOTAL_TIME @CARP_NOT $GRAND_TOTAL/; #use Scalar::Util 'blessed'; use GT::File::Tools qw/mkpath dirname/; use Carp; @CARP_NOT = 'GT::Plugins'; # Inherit from base class for debug and error methods @Plugins::SocialMedia::ISA = qw(GT::Base); # Your code begins here. # PLUGIN HOOKS # =================================================================== sub post_twitter { my $link = shift; $link = format_link($link); $link || return; # When no authentication is required: my $nt = Net::Twitter->new(legacy => 0); my $consumer_key = "u4xwsqHZBKrdWYPrKCm8Lw"; my $consumer_secret = "vjDF6FjeoPJW0WVwgqMEJeuJzilgSIu5QbPGQnWrMI"; my $token = "1921299666-fMicJMBunBjgBb4ieszHo6tYV0mQcbbaMZU5wSB"; my $token_secret = "Ko9gPpBaLxqQj6u68EWdlgnPinGSseVzrzUvytWric"; #return { error => 'error posting...unknown' }; my $tags = $IN->param('twitter_hash_tags'); $tags =~ s/\s+//g; my @tags = split(',',$tags); my $newtags; for (@tags) { $newtags .= ' #' . $_; } my $post_title = $IN->param('twitter_status'); my $len = length $link->{detailed_url}; $len = 140 - 5 - $len; $post_title = substr($post_title,0,$len) . "..." if (length $post_title > $len); $post_title .= " " . $link->{detailed_url}; $post_title .= $newtags; # As of 13-Aug-2010, Twitter requires OAuth for authenticated requests $nt = Net::Twitter->new( traits => [qw/API::RESTv1_1/], consumer_key => $consumer_key, consumer_secret => $consumer_secret, access_token => $token, access_token_secret => $token_secret, ); my $result; my $res = eval { $result = $nt->update($post_title) }; if ( my $err = $@ ) { die $@ unless $err and $err->isa('Net::Twitter::Error'); #warn "HTTP Response Code: ", $err->code, "\n", #"HTTP Message......: ", $err->message, "\n", #"Twitter error.....: ", $err->error, "\n"; $result->{error} = $err->error; } return $result; } sub post_facebook { # ------------------------------------------------------------------- my $access_token = shift; my $id = shift; my $link = format_link($id); if ($link->{facebook_hashtags}) { $link->{Description} .= " "; $link->{Description} .= $link->{facebook_hashtags}; } #Publish to a facebook page as admin my $cfg = Links::Plugins::get_plugin_user_cfg('Auth_Facebook'); my $result = facebook_graph_api('/' . $cfg->{fb_fanpageid} . '/links',{ link => "http://www.slowtwitch.com/temp/$id.html", #link => $link->{detailed_url}, message => $link->{Description}, picture => $link->{Picture}, access_token => $access_token, method => 'post' }); return $result; } sub facebook_graph_api { # ------------------------------------------------------------------- my $uri = new URI('https://graph.facebook.com/' . shift); $uri->query_form(shift); require GT::WWW; my $www = GT::WWW->get("$uri"); my $resp = $www->content; return defined $resp ? decode_json($resp) : undef; } sub format_link { # ------------------------------------------------------------------- my $link = shift; if ($link =~ /^(\d+)$/) { $link = $DB->table('Links')->get($link); } $link or return; $link = Links::SiteHTML::tags('link',$link); my $len = length $link->{detailed_url}; $len = 140 - 5 - $len; $link->{Description} = substr($link->{Description},0,$len) . "..." if (length $link->{Description} > $len); my @cats = $DB->table('Category', 'CatLinks')->select('Category.Full_Name', { 'CatLinks.LinkID' => $link->{ID} })->fetchall_list; $link->{Category} = join "\n", sort @cats; use Plugins::SlideShow; my $paths = Plugins::SlideShow::generate_paths($link->{ID}); $link->{image_paths} = $paths; my $img = $link->{facebook_published_image} || "Image1"; $link->{Picture} = $paths->{$img . '_largest_path'} if ($paths->{$img . '_largest_path'}); return $link; } sub publish_it { # ------------------------------------------------------------------- if ($IN->param('build')) { my $linkid = $IN->param('linkid'); my ($twitter, $fb); use Time::HiRes; #Time::HiRes::sleep(5); #.1 seconds #print $IN->header; $IN->param('t','dev'); #FIXME #print $IN->header; build_it($linkid); my $hash = {}; #print Links::user_page('add_success_publish.html', { success => '1', built => 1, twitter => $twitter, %$hash }); #return; if ($IN->param('post_facebook')) { if ($IN->param('post_twitter')) { my $res = post_twitter($linkid); if ($res->{id}) { $DB->table('Links')->update({ twitter_published => $res->{id} }, { ID => $linkid }); $hash->{twitter_published} = 1; } else { $DB->table('Links')->update({ twitter_published_message => $res->{error} }, { ID => $linkid }); $hash->{twitter_published_message} = $res->{error} if ($res->{error}); } } use Plugins::Auth_Facebook; Plugins::Auth_Facebook::user_auth($linkid); return; } elsif ($IN->param('post_twitter')) { my $res = post_twitter($linkid); if ($res->{id}) { $DB->table('Links')->update({ twitter_published => $res->{id} }, { ID => $linkid }); $hash->{twitter_published} = 1; } else { $DB->table('Links')->update({ twitter_published_message => $res->{error} }, { ID => $linkid }); $hash->{twitter_published_message} = $res->{error} if ($res->{error}); } } $IN->param('t','dev'); #FIXME print $IN->header; print Links::user_page('add_success_publish.html', { success => '1', built => 1, twitter => $twitter, %$hash }); } else { print $IN->header; print Links::user_page('error.html', { error => 'no error' }); } } sub build_it { # ------------------------------------------------------------------- # my $linkid = shift || return; my $unix_time = $CFG->{last_build} ? $CFG->{last_build} : time; Links::init_date(); my $time = GT::Date::date_get($unix_time - $CFG->{date_offset} * 3600, '%yyyy%-%mm%-%dd% %HH%:%MM%:%ss%'); =tag # Do any backups. _build_backup(); # Update isNew, isCool, isPopular flags. _build_reset_hits(); _build_new_flags(); _build_changed_flags(); _build_cool_flags(); # Build Home Page. $PLG->dispatch('create_home', \&_build_home, {}); =cut =tag # Build New Page. $PLG->dispatch('create_new', \&_build_new, {}); # Build Cool Page. $PLG->dispatch('create_cool', \&_build_cool, {}); # Build Ratings Page. $PLG->dispatch('create_ratings', \&_build_ratings, {}); =cut # Build Changed Detailed Page. $PLG->dispatch('create_detailed_changed', \&_build_detailed, GT::SQL::Condition->new('Links.ID', '=', $linkid)); =tag # Build Changed Category Pages. $PLG->dispatch('create_category_changed', \&_build_category, GT::SQL::Condition->new('Timestmp', '>', $time)); $CFG->{last_build} = time; $CFG->save; =cut } sub get_path { # ------------------------------------------------------------------- # my $field = shift; my $filefield = $field . "_largest_path"; my $link = GT::Template->tags(); if (!$link->{$filefield}) { $filefield = $field . "_medium_path"; } return $link->{$filefield}; } # ------------------------------------------------------------------ # MISC BUILD functions # ------------------------------------------------------------------ sub _build_home { # ------------------------------------------------------------------ # Generate the home page. # _time_start(); my $index = $CFG->{build_home} || $CFG->{build_index}; my $page = "$CFG->{build_root_path}/$index"; print $USE_HTML ? qq'Building Home Page...\n' : qq'Building Home Page...\n'; my $fh = _open_write($page); print $fh Links::Build::build(home => {}); close $fh; my $perms = oct $CFG->{build_file_per}; chmod $perms, $page; _display_time(); } sub _build_detailed { # ------------------------------------------------------------------ # Generate one html page per link. # require Links::Tools; my ($cond, $cust_page, $cust_limit); if (ref $_[0] eq 'HASH') { $cust_page = $_[0]->{page}; $cust_limit = $_[0]->{limit}; } else { $cond = shift; } unless ($CFG->{build_detailed}) { print "Skipping Detailed Build (disabled).\n\n"; return; } _time_start(); #print "Building Detailed pages...\n"; # Only build validated links $cond ||= GT::SQL::Condition->new; $cond->add(VIEWABLE); # Loop through, building 1000 at a time my ($limit, $offset, $count, $second_pass) = (1000, 0, 0); my $rel = $DB->table(qw/Links CatLinks Category/); #print "\t"; my $Links = $DB->table('Links'); while () { # Links can be in multiple categories, make sure their detailed pages are only built once $rel->select_options("GROUP BY LinkID") if $CFG->{build_detail_format} eq '%ID%'; $rel->select_options("ORDER BY LinkID"); if ($cust_page or $cust_limit) { last if $second_pass++; $rel->select_options(sprintf "LIMIT %d OFFSET %d", $cust_limit, ($cust_page-1) * $cust_limit); } else { $rel->select_options(sprintf "LIMIT %d OFFSET %d", $limit, $offset*$limit); } my %links_cols = %{$Links->cols}; # Only select Category columns that don't conflict with Links columns. my @cat_cols = grep !$links_cols{$_}, keys %{$DB->table('Category')->cols}; my $sth = $rel->select('Links.*', @cat_cols, 'CategoryID' => $cond); last unless $sth->rows; while (my $link = $sth->fetchrow_hashref) { my $format = $Links->detailed_url($link); $format = "temp/" . $link->{ID} . ".html"; #FIXME my $page = "$CFG->{build_detail_path}/$format"; my $url = "$CFG->{build_detail_url}/$format"; { my $fh = _open_write($page); print $fh Links::Build::build(detailed => $link); } my $perms = oct $CFG->{build_file_per}; chmod $perms, $page; =tag $USE_HTML ? print qq'$link->{ID} ' : print "$link->{ID} "; print "\n\t" if ++$count % 20 == 0; =cut } $offset++; } =tag print "\n"; _display_time(); =cut } sub _build_category { # ------------------------------------------------------------------ # Generate the category pages. # my ($cond, $cust_page, $cust_limit); if (ref $_[0] eq 'HASH') { $cust_page = $_[0]->{page}; $cust_limit = $_[0]->{offset}; $cond = {}; } else { $cond = shift; } _time_start(); print "Building Category pages...\n\n"; my $Cat = $DB->table('Category'); my $CatLinks = $DB->table('Links', 'CatLinks'); $Cat->select_options('ORDER BY Full_Name'); if (defined $cust_page and $cust_limit) { $Cat->select_options(sprintf "LIMIT %d OFFSET %d", $cust_limit, ($cust_page-1)*$cust_limit); } my $sth = $Cat->select(ID => Full_Name => $cond); while (my ($id, $name) = $sth->fetchrow_array) { my $clean_name = $Cat->as_url($name); my $page = $CFG->{build_root_path} . "/" . $clean_name . '/' . $CFG->{build_index}; my $url = $CFG->{build_root_url} . "/" . $clean_name . '/' . $CFG->{build_index}; print $USE_HTML ? "\tBuilding category $name...\n" : "\tBuilding category $name...\n"; my $total = $CatLinks->count({ 'CatLinks.CategoryID' => $id }, VIEWABLE); print "\t\tLinks: $total\n"; # Do sub-pages if requested. if ($CFG->{build_span_pages}) { my $lpp = $CFG->{build_links_per_page} || 25; my $num_pages = int($total / $lpp); $num_pages++ if $total % $lpp; # Create the main page. { my $fh = _open_write($page); print $fh Links::Build::build(category => { id => $id, nh => 1, mh => $lpp }); } my $perms = oct $CFG->{build_file_per}; chmod $perms, $page; # Create the sub pages. for (2 .. $num_pages) { $page = "$CFG->{build_root_path}/$clean_name/$CFG->{build_more}$_$CFG->{build_extension}"; $url = "$CFG->{build_root_url}/$clean_name/$CFG->{build_more}$_$CFG->{build_extension}"; print "\t\tBuilding subpage: " . ($USE_HTML ? "$_\n" : "$_\n" ); { my $fh = _open_write($page); print $fh Links::Build::build(category => { id => $id, nh => $_, mh => $lpp }); } chmod $perms, $page; } } else { { my $fh = _open_write($page); print $fh Links::Build::build(category => { id => $id }); } my $perms = oct $CFG->{build_file_per}; chmod $perms, $page; } print "\tDone\n\n"; } _display_time("Finished building categories"); } sub _build_backup { # ------------------------------------------------------------------ # Create a backup file in our backup directory. # if (! $CFG->{build_use_backup}) { print "Creating backup file... skipped\n\n"; return; } _time_start(); print "Creating backup file...\n"; require Links::Import::S2BK; my $max_keep = 7; my $root = $CFG->{admin_root_path} . '/backup'; my $filename = 'BACKUP'; for my $n (reverse 0 .. $max_keep) { my $oldname = join '.', $filename, $n || (); my $newname = join '.', $filename, $n+1; if (-e "$root/$oldname") { rename "$root/$oldname", "$root/$newname" or print "\tCouldn't rename '$root/$oldname' -> '$root/$newname': $!"; } } Links::Import::S2BK::import({ source => "$CFG->{admin_root_path}/defs", destination => "$root/$filename", delimiter => "\t" }, sub { print "\n\tWARNING: @_\n" }, sub { die @_ }, sub { print "\n\tWARNING: @_\n" }, sub { }); _display_time(); } sub _build_reset_hits { # ------------------------------------------------------------------ # Updates the What's New flags. # _time_start(); print "Resetting hits and rates...\n"; my $ret = Links::Build::build(reset_hits => shift || {}); _display_time(); return $ret; } sub _build_new_flags { # ------------------------------------------------------------------ # Updates the What's New flags. # _time_start(); print "Updating new flags...\n"; my $ret = Links::Build::build(new_flags => shift || {}); _display_time(); return $ret; } sub _build_changed_flags { # ------------------------------------------------------------------ # Updates the isChanged flags. # _time_start(); print "Updating changed flags...\n"; my $ret = Links::Build::build(changed_flags => shift || {}); _display_time(); return $ret; } sub _build_cool_flags { # ------------------------------------------------------------------ # Updates the What's Cool flags. # _time_start(); print "Updating Cool Flags...\n"; my $ret = Links::Build::build(cool_flags => shift || {}); _display_time(); return $ret; } sub _time_start { # ------------------------------------------------------------------ # Start a timer. # $TIME_START = time; } sub _display_time { # ------------------------------------------------------------------ # Return time results. # my $message = shift || 'Done'; return; printf "%s (%.2fs)\n\n", $message, time - $TIME_START; } sub _header { # ------------------------------------------------------------------ # Print intro. # my ($msg, $msg2, $refresh, $started) = @_; my $time = scalar localtime; $refresh ||= ''; $TOTAL_TIME = $started || time; $refresh &&= ""; if ($USE_HTML) { print $IN->header(-nph => $CFG->{nph_headers}); print < $refresh Building HTML Pages BUILDING print Links::header("Building HTML Pages: $msg", $msg2, 0); print <Started at $time. STARTED } else { print "Started at $time.\n\nBuilding HTML pages...\n\n"; } } sub _footer { # ------------------------------------------------------------------ # Print the footer. # my $end = time; my $elapsed = sprintf "%.2f", $end - $TOTAL_TIME; print "All done. Total time: (${elapsed}s)\n"; print "" if $USE_HTML; } sub _open_write { # ----------------------------------------------------------------------------- # Opens a file for writing (overwriting anything already there), and returns a # filehandle reference. Dies with a more user-friendly error then Links::fatal # if the open fails. Can take a second argument which, if true, will cause the # function _not_ to attempt to make the containing directory. # my ($page, $nomkdir) = @_; unless ($nomkdir) { mkpath(dirname($page), oct $CFG->{build_dir_per}); } my $fh = \do { local *FH; *FH }; open $fh, "> $page" and return $fh; my $error = "$!"; my $user = eval { getpwuid($>) } || 'webserver'; if ($error =~ /permission/i) { print "\n\nERROR: Unable to open '$page': $error\n\n"; if (-e $page) { print <