[dw-free] move cgi-bin/lj*.pl files into proper modules (in cgi-bin/LJ)
[commit: http://hg.dwscoalition.org/dw-free/rev/b74e50684390]
http://bugs.dwscoalition.org/show_bug.cgi?id=1726
This file already works like a module, so make the filename follow module
naming conventions, and call it appropriately.
Patch by
kareila.
Files modified:
http://bugs.dwscoalition.org/show_bug.cgi?id=1726
This file already works like a module, so make the filename follow module
naming conventions, and call it appropriately.
Patch by
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Files modified:
- cgi-bin/LJ/Memories.pm
- cgi-bin/ljmemories.pl
- cgi-bin/modperl_subs.pl
-------------------------------------------------------------------------------- diff -r 670a563d0295 -r b74e50684390 cgi-bin/LJ/Memories.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cgi-bin/LJ/Memories.pm Thu Sep 22 12:52:49 2011 +0800 @@ -0,0 +1,656 @@ +#!/usr/bin/perl +# This code was forked from the LiveJournal project owned and operated +# by Live Journal, Inc. The code has been modified and expanded by +# Dreamwidth Studios, LLC. These files were originally licensed under +# the terms of the license supplied by Live Journal, Inc, which can +# currently be found at: +# +# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt +# +# In accordance with the original license, this code and all its +# modifications are provided under the GNU General Public License. +# A copy of that license can be found in the LICENSE file included as +# part of this distribution. + +package LJ::Memories; +use strict; + +# <LJFUNC> +# name: LJ::Memories::count +# class: web +# des: Returns the number of memories that a user has. +# args: uuobj +# des-uuobj: Userid or user object to count memories of. +# returns: Some number; undef on error. +# </LJFUNC> +sub count { + my $u = shift; + $u = LJ::want_user($u); + return undef unless $u; + + # check memcache first + my $count = LJ::MemCache::get([$u->{userid}, "memct:$u->{userid}"]); + return $count if $count; + + # now count + my $dbcr = LJ::get_cluster_def_reader( $u ); + $count = $dbcr->selectrow_array( 'SELECT COUNT(*) FROM memorable2 WHERE userid = ?', + undef, $u->{userid} ); + return undef if $dbcr->err; + + $count += 0; + + # now put in memcache and return it + my $expiration = $LJ::MEMCACHE_EXPIRATION{'memct'} || 43200; # 12 hours + LJ::MemCache::set([$u->{userid}, "memct:$u->{userid}"], $count, $expiration); + return $count; +} + +# <LJFUNC> +# name: LJ::Memories::create +# class: web +# des: Create a new memory for a user. +# args: uuobj, opts, kwids? +# des-uuobj: User id or user object to insert memory for. +# des-opts: Hashref of options that define the memory; keys = journalid, ditemid, des, security. +# des-kwids: Optional; arrayref of keyword ids to categorize this memory under. +# returns: 1 on success, undef on error +# </LJFUNC> +sub create { + my ($u, $opts, $kwids) = @_; + $u = LJ::want_user($u); + return undef unless $u && %{$opts || {}}; + + # make sure we got enough options + my ( $userid, $journalid, $ditemid, $des, $security ) = + ( $u->userid, map { $opts->{$_} } qw(journalid ditemid des security) ); + $userid += 0; + $journalid += 0; + $ditemid += 0; + $security ||= 'public'; + $kwids ||= [ $u->get_keyword_id( '*' ) ]; # * means no category + $des = LJ::trim($des); + return undef unless $userid && $journalid && $ditemid && $des && $security && @$kwids; + return undef unless $security =~ /^(?:public|friends|private)$/; + + # we have valid data, now let's insert it + return undef unless $u->writer; + + # allocate memory id to use + my $memid = LJ::alloc_user_counter( $u, 'R' ); + return undef unless $memid; + + # insert main memory + $u->do( "INSERT INTO memorable2 (userid, memid, journalid, ditemid, des, security) " . + "VALUES (?, ?, ?, ?, ?, ?)", undef, $userid, $memid, $journalid, $ditemid, $des, $security ); + return undef if $u->err; + + # insert keywords + my $val = join ',', map { "($userid, $memid, $_)" } @$kwids; + $u->do( "REPLACE INTO memkeyword2 (userid, memid, kwid) VALUES $val" ); + + + # Delete the appropriate memcache entries + LJ::MemCache::delete( [$userid, "memct:$userid"] ); + my $filter = $journalid == $userid ? 'own' : 'other'; + my $filter_char = _map_filter_to_char($filter); + my $security_char = _map_security_to_char($security); + my $memcache_key = "memkwcnt:$userid:$filter_char:$security_char"; + LJ::MemCache::delete( [$userid, $memcache_key] ); + + return 1; +} + +# <LJFUNC> +# name: LJ::Memories::delete_by_id +# class: web +# des: Deletes a bunch of memories by memid. +# args: uuobj, memids +# des-uuobj: User id or user object to delete memories of. +# des-memids: Arrayref of memids. +# returns: 1 on success; undef on error. +# </LJFUNC> +sub delete_by_id { + my ( $u, $memids ) = @_; + $u = LJ::want_user( $u ); + $memids = [ $memids ] if $memids && !ref $memids; # so they can just pass a single thing... + return undef unless $u && @{$memids || []}; + + # delete actual memory + my $in = join ',', map { $_ + 0 } @$memids; + $u->do("DELETE FROM memorable2 WHERE userid = ? AND memid IN ($in)", undef, $u->{userid}); + return undef if $u->err; + + # delete keyword associations + my $euser = "userid = $u->{userid} AND"; + $u->do("DELETE FROM memkeyword2 WHERE $euser memid IN ($in)"); + + # delete cache of count and keyword counts + clear_memcache($u); + + # success at this point, since the first delete succeeded + return 1; +} + +# <LJFUNC> +# name: LJ::Memories::get_keyword_counts +# class: web +# des: Get a list of keywords and the counts for memories, showing how many memories are under +# each keyword. +# args: uuobj, opts? +# des-uuobj: User id or object of user. +# des-opts: Optional; hashref passed to _memory_getter, suggested keys are security and filter +# if you want to get only certain memories in the keyword list. +# returns: Hashref { kwid => count }; undef on error +# </LJFUNC> +sub get_keyword_counts { + my ($u, $opts) = @_; + $u = LJ::want_user($u); + return undef unless $u; + my $userid = $u->{userid}; + + my $filter_parm = $opts->{filter}; + my @security_parm = $opts->{security} ? @{$opts->{security}} : (); + + my ($cache_counts, $missing_keys) = _get_memcache_keyword_counts($userid, $filter_parm, @security_parm); + return $cache_counts unless @$missing_keys; + + # Get the user's memories based on filter and security + $opts->{filter_security_pairs} = $missing_keys; + $opts->{notext} = 1; + my $memories = LJ::Memories::_memory_getter($u, $opts); + return undef unless defined $memories; # error case + + # Generate mapping of memid to filter (e.g. own) and security (e.g. private) + my (%mem_filter, @all_memids); + foreach my $memid (keys %$memories) { + push @all_memids, $memid; + my $memory_filter = $memories->{$memid}->{journalid} == $userid ? 'own' : 'other'; + my $memory_security = $memories->{$memid}->{security}; + $mem_filter{$memid} = [$memory_filter, $memory_security]; + } + + # now let's get the keywords these memories use + my $mem_kw_rows; + + if (@all_memids) { + my $in = join ',', @all_memids; + my $dbcr = LJ::get_cluster_reader( $u ); + my $sql = "SELECT kwid, memid FROM memkeyword2 WHERE userid = $userid AND memid IN ($in)"; + $mem_kw_rows = $dbcr->selectall_arrayref( $sql ); + return undef if $dbcr->err; + } + + # Filter and Sum + my %counts; + foreach my $row (@{$mem_kw_rows||[]}) { + my ($kwid, $memid) = @$row; + my ($filter, $security) = @{$mem_filter{$memid}}; + $counts{$filter}{$security}{$kwid}++; + } + + # Add these new counts to our memcache counts to get totals + my $output_counts = $cache_counts; + foreach my $filter (keys %counts) { + foreach my $security (keys %{$counts{$filter}}) { + if ($counts{$filter}{$security}) { + add_hash($output_counts, $counts{$filter}{$security}); + } + } + } + + # Create empty anonymous hashes for missing key combos + foreach my $missing_key (@$missing_keys) { + my ($missing_filter, $missing_security) = split /-/, $missing_key; + next if exists $counts{$missing_filter}{$missing_security}; + $counts{$missing_filter}{$missing_security} = {}; + } + + # Store memcache entries with counts + foreach my $filter (qw/own other/) { + foreach my $security (qw/friends private public/) { + next unless exists $counts{$filter}{$security}; + my $filter_char = _map_filter_to_char($filter); + my $security_char = _map_security_to_char($security); + my $memcache_key = "memkwcnt:$userid:$filter_char:$security_char"; + my $expiration = $LJ::MEMCACHE_EXPIRATION{'memkwcnt'} || 86400; + LJ::MemCache::set([$userid, $memcache_key], $counts{$filter}{$security}, $expiration); + } + } + + return $output_counts; +} + +# +# Name: _map_security_to_char +# API: Private to this module +# Description: Map a verbose security name to a single character +# Parameter: Verbose security name +# Return: Single character representation of security +# +sub _map_security_to_char { + my $verbose_security = shift; + my %security_map = (friends => 'f', private => 'v', public => 'u'); + return $security_map{$verbose_security} || die "Can't map security '" . LJ::ehtml($verbose_security) . "' to character"; +} + +# +# Name: _map_filter_to_char +# API: Private to this module +# Description: Map a verbose filter name to a single character +# Parameter: Verbose filter name +# Return: Single character representation of filter +# +sub _map_filter_to_char { + my $verbose_filter = shift; + my %filter_map = (own => 'w', other => 't'); + return $filter_map{$verbose_filter} || die "Can't map filter '" . LJ::ehtml($verbose_filter) . "' to character"; +} + +# +# Name: _get_memcache_keyword_counts +# +# API: Private to this module +# +# Description: +# - Get keyword counts from memcache based on user, filter, and security +# - Return hash of counts and array of missing keys +# +# Parameters: +# - $userid = ID of the User +# - $filter_parm = {own|other} +# - @security_parm = array of values {friends|private|public} - () = all +# +# Return Values: +# - HashRef of counts by Keyword ID +# - ArrayRef of missing keys (e.g. 'owner-private') +# +sub _get_memcache_keyword_counts { + my ($userid, $filter_parm, @security_parm) = @_; + + # Build up the memcache keys that we're looking for + my @memcache_keys; + my %filter_security_map; + foreach my $filter (qw/own other/) { + foreach my $security (qw/friends private public/) { + my $filter_matches = ($filter_parm eq $filter) || ($filter_parm eq 'all'); + my $security_matches = @security_parm == 0 || grep(/$security/, @security_parm); + next unless $filter_matches && $security_matches; + my $filter_char = _map_filter_to_char($filter); + my $security_char = _map_security_to_char($security); + my $memcache_key = "memkwcnt:$userid:$filter_char:$security_char"; + push @memcache_keys, $memcache_key; + $filter_security_map{"$filter_char:$security_char"} = [$filter, $security]; + } + } + + # Loop over our memcache results, get counts and total them as we go + my (%output_counts, @missing_keys); + my $memcache_counts = LJ::is_enabled('memkwcnt_memcaching') ? LJ::MemCache::get_multi(map { [$userid, $_] } @memcache_keys) : {}; + foreach my $memcache_key (@memcache_keys) { + my $counts = $memcache_counts->{$memcache_key}; + if ($counts) { # Add these memcache counts to totals + add_hash(\%output_counts, $counts); + } else { + my ($filter_security_chars) = $memcache_key =~ /$userid:(.:.)$/; + my ($filter, $security) = @{$filter_security_map{$filter_security_chars}}; + push @missing_keys, $filter . '-' . $security; + } + } + + return \%output_counts, \@missing_keys; +} + +# <LJFUNC> +# name: LJ::Memories::add_hash +# class: web +# des: Add values of one hash, to the corresponding entries in another. +# args: HashRef1, HashRef2 +# returns: Values are added to the first parameter hash. +# </LJFUNC> +sub add_hash { + my ($hash1, $hash2) = @_; + + while (my ($key,$value) = each %$hash2) { + $hash1->{$key} += $value; + } +} + +# <LJFUNC> +# name: LJ::Memories::get_keywordids +# class: web +# des: Get all keyword ids a user has used for a certain memory. +# args: uuobj, memid +# des-uuobj: User id or user object to check memory of. +# des-memid: Memory id to get keyword ids for. +# returns: Arrayref of keywordids; undef on error. +# </LJFUNC> +sub get_keywordids { + my ( $u, $memid ) = @_; + $u = LJ::want_user( $u ); + $memid += 0; + return undef unless $u && $memid; + + # definitive reader/master because this function is usually called when + # someone is on an edit page. + my $dbcr = LJ::get_cluster_def_reader( $u ); + my $kwids = $dbcr->selectcol_arrayref( 'SELECT kwid FROM memkeyword2 WHERE userid = ? AND memid = ?', + undef, $u->userid, $memid ); + return undef if $dbcr->err; + + + # all good, return + return $kwids; +} + +# <LJFUNC> +# name: LJ::Memories::update_memory +# class: web +# des: Updates the description and security of a memory. +# args: uuobj, memid, updopts +# des-uuobj: User id or user object to update memory of. +# des-memid: Memory id to update. +# des-updopts: Update options; hashref with keys 'des' and 'security', values being what +# you want to update the memory to have. +# returns: 1 on success, undef on error +# </LJFUNC> +# sub update_memory { +# my ($u, $memid, $upd) = @_; +# $u = LJ::want_user($u); +# $memid += 0; +# return unless $u && $memid && %{$upd || {}}; +# +# # get database handle +# my ($db, $table) = ($u, '2'); +# return undef unless $db; +# +# # construct update lines... only valid things we can update are des and security +# my @updates; +# my $security_updated; +# foreach my $what (keys %$upd) { +# next unless $what =~ m/^(?:des|security)$/; +# $security_updated = 1 if $what eq 'security'; +# push @updates, "$what=" . $db->quote($upd->{$what}); +# } +# my $updstr = join ',', @updates; +# +# # now perform update +# $db->do("UPDATE memorable$table SET $updstr WHERE userid = ? AND memid = ?", +# undef, $u->{userid}, $memid); +# return undef if $db->err; +# +# # Delete memcache entries if the security of the memory was updated +# clear_memcache($u) if $security_updated; +# +# return 1; +# } + +# this messy function gets memories based on an options hashref. this is an +# API API and isn't recommended for use by BML etc... add to the API and have +# API functions call this if needed. +# +# options in $opts hashref: +# security => [ 'public', 'private', ... ], or some subset thereof +# filter => 'all' | 'own' | 'other', filter -- defaults to all +# filter_security_pairs => [ 'own-private', ... ], Pairs of filter/security +# notext => 1/0, if on, do not load/return description field +# byid => [ 1, 2, 3, ... ], load memories by *memid* +# byditemid => [ 1, 2, 3 ... ], load by ditemid (MUST specify journalid too) +# journalid => 1, find memories by ditemid (see above) for this journalid +# +# note that all memories are loaded from a single user, specified as the first +# parameter. does not let you load memories from more than one user. +sub _memory_getter { + my ($u, $opts) = @_; + $u = LJ::want_user($u); + $opts ||= {}; + return undef unless $u; + + # Specify filter/security by pair, or individually + my $secwhere = ''; + my $extrawhere; + if ($opts->{filter_security_pairs}) { + my @pairs; + foreach my $filter_security_pair (@{$opts->{filter_security_pairs}}) { + my ($filter, $security) = $filter_security_pair =~ /^(\w+)-(\w+)$/; + my $filter_predicate = ($filter eq 'all') ? '' : 'journalid' . ($filter eq 'own' ? '=' : '<>') . $u->{userid}; + push @pairs, "($filter_predicate AND security='$security')"; + } + $secwhere = 'AND (' . join(' OR ', @pairs) . ')'; + } else { + if (@{$opts->{security} || []}) { + my @secs; + foreach my $sec (@{$opts->{security}}) { + push @secs, $sec + if $sec =~ /^(?:public|friends|private)$/; + } + $secwhere = "AND security IN (" . join(',', map { "'$_'" } @secs) . ")"; + } + if ($opts->{filter} eq 'all') { $extrawhere = ''; } + elsif ($opts->{filter} eq 'own') { $extrawhere = "AND journalid = $u->{userid}"; } + elsif ($opts->{filter} eq 'other') { $extrawhere = "AND journalid <> $u->{userid}"; } + } + + my $des = $opts->{notext} ? '' : 'des, '; + my $selwhere; + if (@{$opts->{byid} || []}) { + # they want to get some explicit memories by memid + my $in = join ',', map { $_+0 } @{$opts->{byid}}; + $selwhere = "AND memid IN ($in)"; + } elsif ($opts->{byditemid} && $opts->{journalid}) { + # or, they want to see if a memory exists for a particular item + my $selitemid = "ditemid"; + $opts->{byditemid} += 0; + $opts->{journalid} += 0; + $selwhere = "AND journalid = $opts->{journalid} AND $selitemid = $opts->{byditemid}"; + } elsif ($opts->{byditemid}) { + # get memory, OLD STYLE so journalid is 0 + my $selitemid = "ditemid"; + $opts->{byditemid} += 0; + $selwhere = "AND journalid = 0 AND $selitemid = $opts->{byditemid}"; + } + + # load up memories into hashref + my ( %memories, $sth ); + my $dbcr = LJ::get_cluster_reader( $u ); + my $sql = "SELECT memid, userid, journalid, ditemid, $des security " + . "FROM memorable2 WHERE userid = ? $selwhere $secwhere $extrawhere"; + $sth = $dbcr->prepare( $sql ); + + # general execution and fetching for return + $sth->execute($u->{userid}); + return undef if $sth->err; + while ($_ = $sth->fetchrow_hashref()) { + # we have to do this ditemid->jitemid to make old code work, + # but this can probably go away at some point... + if (defined $_->{ditemid}) { + $_->{jitemid} = $_->{ditemid}; + } else { + $_->{ditemid} = $_->{jitemid}; + } + $memories{$_->{memid}} = $_; + } + + my @jids = map { $_->{journalid} } values %memories; + my $us = LJ::load_userids(@jids); + foreach my $mem (values %memories) { + next unless $mem->{journalid}; + $mem->{user} = $us->{$mem->{journalid}}->user; + } + + return \%memories; +} + +# <LJFUNC> +# name: LJ::Memories::get_by_id +# class: web +# des: Get memories given some memory ids. +# args: uuobj, memids +# des-uuobj: User id or user object to get memories for. +# des-memids: The rest of the memory ids. Array. (Pass them in as individual parameters...) +# returns: Hashref of memories with keys being memid; undef on error. +# </LJFUNC> +# sub get_by_id { +# my $u = shift; +# return {} unless @_; # make sure they gave us some ids +# +# # pass to getter to get by id +# return LJ::Memories::_memory_getter($u, { byid => [ map { $_+0 } @_ ] }); +# } + +# <LJFUNC> +# name: LJ::Memories::get_by_ditemid +# class: web +# des: Get memory for a given journal entry. +# args: uuobj, journalid, ditemid +# des-uuobj: User id or user object to get memories for. +# des-journalid: Userid for journal entry is in. +# des-ditemid: Display itemid of entry. +# returns: Hashref of individual memory. +# </LJFUNC> +sub get_by_ditemid { + my ($u, $jid, $ditemid) = @_; + $jid += 0; + $ditemid += 0; + return undef unless $ditemid; # _memory_getter checks $u and $jid isn't necessary + # because this might be an old-style memory + + # pass to getter with appropriate options + my $memhash = LJ::Memories::_memory_getter($u, { byditemid => $ditemid, journalid => $jid }); + return undef unless %{$memhash || {}}; + return [ values %$memhash ]->[0]; # ugly +} + +# <LJFUNC> +# name: LJ::Memories::get_by_user +# class: web +# des: Get memories given a user. +# args: uuobj +# des-uuobj: User id or user object to get memories for. +# returns: Hashref of memories with keys being memid; undef on error. +# </LJFUNC> +# sub get_by_user { +# # simply passes through to _memory_getter +# return LJ::Memories::_memory_getter(@_); +# } + +# <LJFUNC> +# name: LJ::Memories::get_by_keyword +# class: web +# des: Get memories given a user and a keyword/keyword id. +# args: uuobj, kwoid, opts +# des-uuobj: User id or user object to get memories for. +# des-kwoid: Keyword (string) or keyword id (number) to get memories for. +# des-opts: Hashref of extra options to pass through to memory getter. Suggested options +# are filter and security for limiting the memories returned. +# returns: Hashref of memories with keys being memid; undef on error. +# </LJFUNC> +sub get_by_keyword { + my ($u, $kwoid, $opts) = @_; + $u = LJ::want_user($u); + my $kwid = $kwoid+0; + my $kw = defined $kwoid && !$kwid ? $kwoid : undef; + return undef unless $u && ($kwid || defined $kw); + + my $memids; + my $dbcr = LJ::get_cluster_reader( $u ); + return undef unless $dbcr; + + # get keyword id if we don't have it + if ( defined $kw ) { + $kwid = $dbcr->selectrow_array( 'SELECT kwid FROM userkeywords WHERE userid = ? AND keyword = ?', + undef, $u->userid, $kw ) + 0; + } + return undef unless $kwid; + + # now get the actual memory ids + $memids = $dbcr->selectcol_arrayref( 'SELECT memid FROM memkeyword2 WHERE userid = ? AND kwid = ?', + undef, $u->{userid}, $kwid ); + return undef if $dbcr->err; + + # return + $memids = [] unless defined($memids); + my $memories = @$memids > 0 ? LJ::Memories::_memory_getter($u, {%{$opts || {}}, byid => $memids }) : {}; + return $memories; +} + +# <LJFUNC> +# name: LJ::Memories::get_keywords +# class: +# des: Retrieves keyword/keyids without big joins, returns a hashref. +# args: uobj +# des-uobj: User object to get keyword pairs for. +# returns: Hashref; { keywordid => keyword } +# </LJFUNC> +sub get_keywords { + my $u = shift; + $u = LJ::want_user($u); + return undef unless $u; + + my $use_reader = 0; + my $memkey = [$u->{userid},"memkwid:$u->{userid}"]; + my $ret = LJ::MemCache::get($memkey); + return $ret if defined $ret; + $ret = {}; + + my $dbcm = LJ::get_cluster_def_reader( $u ); + unless ( $dbcm ) { + $use_reader = 1; + $dbcm = LJ::get_cluster_reader( $u ); + } + my $ids = $dbcm->selectcol_arrayref( 'SELECT DISTINCT kwid FROM memkeyword2 WHERE userid = ?', + undef, $u->userid ); + if ( @{$ids || []} ) { + my $in = join ",", @$ids; + my $rows = $dbcm->selectall_arrayref( 'SELECT kwid, keyword FROM userkeywords ' . + "WHERE userid = ? AND kwid IN ($in)", + undef, $u->userid ); + $ret->{$_->[0]} = $_->[1] foreach @{$rows || []}; + } + + my $expiration = $LJ::MEMCACHE_EXPIRATION{'memkwid'} || 86400; + LJ::MemCache::set($memkey, $ret, $expiration) unless $use_reader; + return $ret; +} + +# <LJFUNC> +# name: LJ::Memories::updated_keywords +# class: web +# des: Deletes memcached keyword data. +# args: uobj +# des-uobj: User object to clear memcached keywords for. +# returns: undef. +# </LJFUNC> +sub updated_keywords { + return clear_memcache(shift); +} + +# <LJFUNC> +# name: LJ::Memories::clear_memcache +# class: web +# des: Deletes memcached keyword data. +# args: uobj +# des-uobj: User object to clear memcached keywords for. +# returns: undef. +# </LJFUNC> +sub clear_memcache { + my $u = shift; + return unless ref $u; + my $userid = $u->{userid}; + + LJ::MemCache::delete([$userid, "memct:$userid"]); + + LJ::MemCache::delete([$userid, "memkwid:$userid"]); + + # Delete all memkwcnt entries + LJ::MemCache::delete([$userid, "memkwcnt:$userid:w:f"]); + LJ::MemCache::delete([$userid, "memkwcnt:$userid:w:v"]); + LJ::MemCache::delete([$userid, "memkwcnt:$userid:w:u"]); + LJ::MemCache::delete([$userid, "memkwcnt:$userid:t:f"]); + LJ::MemCache::delete([$userid, "memkwcnt:$userid:t:v"]); + LJ::MemCache::delete([$userid, "memkwcnt:$userid:t:u"]); + + return undef; +} + +1; diff -r 670a563d0295 -r b74e50684390 cgi-bin/ljmemories.pl --- a/cgi-bin/ljmemories.pl Thu Sep 22 12:46:51 2011 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,656 +0,0 @@ -#!/usr/bin/perl -# This code was forked from the LiveJournal project owned and operated -# by Live Journal, Inc. The code has been modified and expanded by -# Dreamwidth Studios, LLC. These files were originally licensed under -# the terms of the license supplied by Live Journal, Inc, which can -# currently be found at: -# -# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt -# -# In accordance with the original license, this code and all its -# modifications are provided under the GNU General Public License. -# A copy of that license can be found in the LICENSE file included as -# part of this distribution. - -package LJ::Memories; -use strict; - -# <LJFUNC> -# name: LJ::Memories::count -# class: web -# des: Returns the number of memories that a user has. -# args: uuobj -# des-uuobj: Userid or user object to count memories of. -# returns: Some number; undef on error. -# </LJFUNC> -sub count { - my $u = shift; - $u = LJ::want_user($u); - return undef unless $u; - - # check memcache first - my $count = LJ::MemCache::get([$u->{userid}, "memct:$u->{userid}"]); - return $count if $count; - - # now count - my $dbcr = LJ::get_cluster_def_reader( $u ); - $count = $dbcr->selectrow_array( 'SELECT COUNT(*) FROM memorable2 WHERE userid = ?', - undef, $u->{userid} ); - return undef if $dbcr->err; - - $count += 0; - - # now put in memcache and return it - my $expiration = $LJ::MEMCACHE_EXPIRATION{'memct'} || 43200; # 12 hours - LJ::MemCache::set([$u->{userid}, "memct:$u->{userid}"], $count, $expiration); - return $count; -} - -# <LJFUNC> -# name: LJ::Memories::create -# class: web -# des: Create a new memory for a user. -# args: uuobj, opts, kwids? -# des-uuobj: User id or user object to insert memory for. -# des-opts: Hashref of options that define the memory; keys = journalid, ditemid, des, security. -# des-kwids: Optional; arrayref of keyword ids to categorize this memory under. -# returns: 1 on success, undef on error -# </LJFUNC> -sub create { - my ($u, $opts, $kwids) = @_; - $u = LJ::want_user($u); - return undef unless $u && %{$opts || {}}; - - # make sure we got enough options - my ( $userid, $journalid, $ditemid, $des, $security ) = - ( $u->userid, map { $opts->{$_} } qw(journalid ditemid des security) ); - $userid += 0; - $journalid += 0; - $ditemid += 0; - $security ||= 'public'; - $kwids ||= [ $u->get_keyword_id( '*' ) ]; # * means no category - $des = LJ::trim($des); - return undef unless $userid && $journalid && $ditemid && $des && $security && @$kwids; - return undef unless $security =~ /^(?:public|friends|private)$/; - - # we have valid data, now let's insert it - return undef unless $u->writer; - - # allocate memory id to use - my $memid = LJ::alloc_user_counter( $u, 'R' ); - return undef unless $memid; - - # insert main memory - $u->do( "INSERT INTO memorable2 (userid, memid, journalid, ditemid, des, security) " . - "VALUES (?, ?, ?, ?, ?, ?)", undef, $userid, $memid, $journalid, $ditemid, $des, $security ); - return undef if $u->err; - - # insert keywords - my $val = join ',', map { "($userid, $memid, $_)" } @$kwids; - $u->do( "REPLACE INTO memkeyword2 (userid, memid, kwid) VALUES $val" ); - - - # Delete the appropriate memcache entries - LJ::MemCache::delete( [$userid, "memct:$userid"] ); - my $filter = $journalid == $userid ? 'own' : 'other'; - my $filter_char = _map_filter_to_char($filter); - my $security_char = _map_security_to_char($security); - my $memcache_key = "memkwcnt:$userid:$filter_char:$security_char"; - LJ::MemCache::delete( [$userid, $memcache_key] ); - - return 1; -} - -# <LJFUNC> -# name: LJ::Memories::delete_by_id -# class: web -# des: Deletes a bunch of memories by memid. -# args: uuobj, memids -# des-uuobj: User id or user object to delete memories of. -# des-memids: Arrayref of memids. -# returns: 1 on success; undef on error. -# </LJFUNC> -sub delete_by_id { - my ( $u, $memids ) = @_; - $u = LJ::want_user( $u ); - $memids = [ $memids ] if $memids && !ref $memids; # so they can just pass a single thing... - return undef unless $u && @{$memids || []}; - - # delete actual memory - my $in = join ',', map { $_ + 0 } @$memids; - $u->do("DELETE FROM memorable2 WHERE userid = ? AND memid IN ($in)", undef, $u->{userid}); - return undef if $u->err; - - # delete keyword associations - my $euser = "userid = $u->{userid} AND"; - $u->do("DELETE FROM memkeyword2 WHERE $euser memid IN ($in)"); - - # delete cache of count and keyword counts - clear_memcache($u); - - # success at this point, since the first delete succeeded - return 1; -} - -# <LJFUNC> -# name: LJ::Memories::get_keyword_counts -# class: web -# des: Get a list of keywords and the counts for memories, showing how many memories are under -# each keyword. -# args: uuobj, opts? -# des-uuobj: User id or object of user. -# des-opts: Optional; hashref passed to _memory_getter, suggested keys are security and filter -# if you want to get only certain memories in the keyword list. -# returns: Hashref { kwid => count }; undef on error -# </LJFUNC> -sub get_keyword_counts { - my ($u, $opts) = @_; - $u = LJ::want_user($u); - return undef unless $u; - my $userid = $u->{userid}; - - my $filter_parm = $opts->{filter}; - my @security_parm = $opts->{security} ? @{$opts->{security}} : (); - - my ($cache_counts, $missing_keys) = _get_memcache_keyword_counts($userid, $filter_parm, @security_parm); - return $cache_counts unless @$missing_keys; - - # Get the user's memories based on filter and security - $opts->{filter_security_pairs} = $missing_keys; - $opts->{notext} = 1; - my $memories = LJ::Memories::_memory_getter($u, $opts); - return undef unless defined $memories; # error case - - # Generate mapping of memid to filter (e.g. own) and security (e.g. private) - my (%mem_filter, @all_memids); - foreach my $memid (keys %$memories) { - push @all_memids, $memid; - my $memory_filter = $memories->{$memid}->{journalid} == $userid ? 'own' : 'other'; - my $memory_security = $memories->{$memid}->{security}; - $mem_filter{$memid} = [$memory_filter, $memory_security]; - } - - # now let's get the keywords these memories use - my $mem_kw_rows; - - if (@all_memids) { - my $in = join ',', @all_memids; - my $dbcr = LJ::get_cluster_reader( $u ); - my $sql = "SELECT kwid, memid FROM memkeyword2 WHERE userid = $userid AND memid IN ($in)"; - $mem_kw_rows = $dbcr->selectall_arrayref( $sql ); - return undef if $dbcr->err; - } - - # Filter and Sum - my %counts; - foreach my $row (@{$mem_kw_rows||[]}) { - my ($kwid, $memid) = @$row; - my ($filter, $security) = @{$mem_filter{$memid}}; - $counts{$filter}{$security}{$kwid}++; - } - - # Add these new counts to our memcache counts to get totals - my $output_counts = $cache_counts; - foreach my $filter (keys %counts) { - foreach my $security (keys %{$counts{$filter}}) { - if ($counts{$filter}{$security}) { - add_hash($output_counts, $counts{$filter}{$security}); - } - } - } - - # Create empty anonymous hashes for missing key combos - foreach my $missing_key (@$missing_keys) { - my ($missing_filter, $missing_security) = split /-/, $missing_key; - next if exists $counts{$missing_filter}{$missing_security}; - $counts{$missing_filter}{$missing_security} = {}; - } - - # Store memcache entries with counts - foreach my $filter (qw/own other/) { - foreach my $security (qw/friends private public/) { - next unless exists $counts{$filter}{$security}; - my $filter_char = _map_filter_to_char($filter); - my $security_char = _map_security_to_char($security); - my $memcache_key = "memkwcnt:$userid:$filter_char:$security_char"; - my $expiration = $LJ::MEMCACHE_EXPIRATION{'memkwcnt'} || 86400; - LJ::MemCache::set([$userid, $memcache_key], $counts{$filter}{$security}, $expiration); - } - } - - return $output_counts; -} - -# -# Name: _map_security_to_char -# API: Private to this module -# Description: Map a verbose security name to a single character -# Parameter: Verbose security name -# Return: Single character representation of security -# -sub _map_security_to_char { - my $verbose_security = shift; - my %security_map = (friends => 'f', private => 'v', public => 'u'); - return $security_map{$verbose_security} || die "Can't map security '" . LJ::ehtml($verbose_security) . "' to character"; -} - -# -# Name: _map_filter_to_char -# API: Private to this module -# Description: Map a verbose filter name to a single character -# Parameter: Verbose filter name -# Return: Single character representation of filter -# -sub _map_filter_to_char { - my $verbose_filter = shift; - my %filter_map = (own => 'w', other => 't'); - return $filter_map{$verbose_filter} || die "Can't map filter '" . LJ::ehtml($verbose_filter) . "' to character"; -} - -# -# Name: _get_memcache_keyword_counts -# -# API: Private to this module -# -# Description: -# - Get keyword counts from memcache based on user, filter, and security -# - Return hash of counts and array of missing keys -# -# Parameters: -# - $userid = ID of the User -# - $filter_parm = {own|other} -# - @security_parm = array of values {friends|private|public} - () = all -# -# Return Values: -# - HashRef of counts by Keyword ID -# - ArrayRef of missing keys (e.g. 'owner-private') -# -sub _get_memcache_keyword_counts { - my ($userid, $filter_parm, @security_parm) = @_; - - # Build up the memcache keys that we're looking for - my @memcache_keys; - my %filter_security_map; - foreach my $filter (qw/own other/) { - foreach my $security (qw/friends private public/) { - my $filter_matches = ($filter_parm eq $filter) || ($filter_parm eq 'all'); - my $security_matches = @security_parm == 0 || grep(/$security/, @security_parm); - next unless $filter_matches && $security_matches; - my $filter_char = _map_filter_to_char($filter); - my $security_char = _map_security_to_char($security); - my $memcache_key = "memkwcnt:$userid:$filter_char:$security_char"; - push @memcache_keys, $memcache_key; - $filter_security_map{"$filter_char:$security_char"} = [$filter, $security]; - } - } - - # Loop over our memcache results, get counts and total them as we go - my (%output_counts, @missing_keys); - my $memcache_counts = LJ::is_enabled('memkwcnt_memcaching') ? LJ::MemCache::get_multi(map { [$userid, $_] } @memcache_keys) : {}; - foreach my $memcache_key (@memcache_keys) { - my $counts = $memcache_counts->{$memcache_key}; - if ($counts) { # Add these memcache counts to totals - add_hash(\%output_counts, $counts); - } else { - my ($filter_security_chars) = $memcache_key =~ /$userid:(.:.)$/; - my ($filter, $security) = @{$filter_security_map{$filter_security_chars}}; - push @missing_keys, $filter . '-' . $security; - } - } - - return \%output_counts, \@missing_keys; -} - -# <LJFUNC> -# name: LJ::Memories::add_hash -# class: web -# des: Add values of one hash, to the corresponding entries in another. -# args: HashRef1, HashRef2 -# returns: Values are added to the first parameter hash. -# </LJFUNC> -sub add_hash { - my ($hash1, $hash2) = @_; - - while (my ($key,$value) = each %$hash2) { - $hash1->{$key} += $value; - } -} - -# <LJFUNC> -# name: LJ::Memories::get_keywordids -# class: web -# des: Get all keyword ids a user has used for a certain memory. -# args: uuobj, memid -# des-uuobj: User id or user object to check memory of. -# des-memid: Memory id to get keyword ids for. -# returns: Arrayref of keywordids; undef on error. -# </LJFUNC> -sub get_keywordids { - my ( $u, $memid ) = @_; - $u = LJ::want_user( $u ); - $memid += 0; - return undef unless $u && $memid; - - # definitive reader/master because this function is usually called when - # someone is on an edit page. - my $dbcr = LJ::get_cluster_def_reader( $u ); - my $kwids = $dbcr->selectcol_arrayref( 'SELECT kwid FROM memkeyword2 WHERE userid = ? AND memid = ?', - undef, $u->userid, $memid ); - return undef if $dbcr->err; - - - # all good, return - return $kwids; -} - -# <LJFUNC> -# name: LJ::Memories::update_memory -# class: web -# des: Updates the description and security of a memory. -# args: uuobj, memid, updopts -# des-uuobj: User id or user object to update memory of. -# des-memid: Memory id to update. -# des-updopts: Update options; hashref with keys 'des' and 'security', values being what -# you want to update the memory to have. -# returns: 1 on success, undef on error -# </LJFUNC> -# sub update_memory { -# my ($u, $memid, $upd) = @_; -# $u = LJ::want_user($u); -# $memid += 0; -# return unless $u && $memid && %{$upd || {}}; -# -# # get database handle -# my ($db, $table) = ($u, '2'); -# return undef unless $db; -# -# # construct update lines... only valid things we can update are des and security -# my @updates; -# my $security_updated; -# foreach my $what (keys %$upd) { -# next unless $what =~ m/^(?:des|security)$/; -# $security_updated = 1 if $what eq 'security'; -# push @updates, "$what=" . $db->quote($upd->{$what}); -# } -# my $updstr = join ',', @updates; -# -# # now perform update -# $db->do("UPDATE memorable$table SET $updstr WHERE userid = ? AND memid = ?", -# undef, $u->{userid}, $memid); -# return undef if $db->err; -# -# # Delete memcache entries if the security of the memory was updated -# clear_memcache($u) if $security_updated; -# -# return 1; -# } - -# this messy function gets memories based on an options hashref. this is an -# API API and isn't recommended for use by BML etc... add to the API and have -# API functions call this if needed. -# -# options in $opts hashref: -# security => [ 'public', 'private', ... ], or some subset thereof -# filter => 'all' | 'own' | 'other', filter -- defaults to all -# filter_security_pairs => [ 'own-private', ... ], Pairs of filter/security -# notext => 1/0, if on, do not load/return description field -# byid => [ 1, 2, 3, ... ], load memories by *memid* -# byditemid => [ 1, 2, 3 ... ], load by ditemid (MUST specify journalid too) -# journalid => 1, find memories by ditemid (see above) for this journalid -# -# note that all memories are loaded from a single user, specified as the first -# parameter. does not let you load memories from more than one user. -sub _memory_getter { - my ($u, $opts) = @_; - $u = LJ::want_user($u); - $opts ||= {}; - return undef unless $u; - - # Specify filter/security by pair, or individually - my $secwhere = ''; - my $extrawhere; - if ($opts->{filter_security_pairs}) { - my @pairs; - foreach my $filter_security_pair (@{$opts->{filter_security_pairs}}) { - my ($filter, $security) = $filter_security_pair =~ /^(\w+)-(\w+)$/; - my $filter_predicate = ($filter eq 'all') ? '' : 'journalid' . ($filter eq 'own' ? '=' : '<>') . $u->{userid}; - push @pairs, "($filter_predicate AND security='$security')"; - } - $secwhere = 'AND (' . join(' OR ', @pairs) . ')'; - } else { - if (@{$opts->{security} || []}) { - my @secs; - foreach my $sec (@{$opts->{security}}) { - push @secs, $sec - if $sec =~ /^(?:public|friends|private)$/; - } - $secwhere = "AND security IN (" . join(',', map { "'$_'" } @secs) . ")"; - } - if ($opts->{filter} eq 'all') { $extrawhere = ''; } - elsif ($opts->{filter} eq 'own') { $extrawhere = "AND journalid = $u->{userid}"; } - elsif ($opts->{filter} eq 'other') { $extrawhere = "AND journalid <> $u->{userid}"; } - } - - my $des = $opts->{notext} ? '' : 'des, '; - my $selwhere; - if (@{$opts->{byid} || []}) { - # they want to get some explicit memories by memid - my $in = join ',', map { $_+0 } @{$opts->{byid}}; - $selwhere = "AND memid IN ($in)"; - } elsif ($opts->{byditemid} && $opts->{journalid}) { - # or, they want to see if a memory exists for a particular item - my $selitemid = "ditemid"; - $opts->{byditemid} += 0; - $opts->{journalid} += 0; - $selwhere = "AND journalid = $opts->{journalid} AND $selitemid = $opts->{byditemid}"; - } elsif ($opts->{byditemid}) { - # get memory, OLD STYLE so journalid is 0 - my $selitemid = "ditemid"; - $opts->{byditemid} += 0; - $selwhere = "AND journalid = 0 AND $selitemid = $opts->{byditemid}"; - } - - # load up memories into hashref - my ( %memories, $sth ); - my $dbcr = LJ::get_cluster_reader( $u ); - my $sql = "SELECT memid, userid, journalid, ditemid, $des security " - . "FROM memorable2 WHERE userid = ? $selwhere $secwhere $extrawhere"; - $sth = $dbcr->prepare( $sql ); - - # general execution and fetching for return - $sth->execute($u->{userid}); - return undef if $sth->err; - while ($_ = $sth->fetchrow_hashref()) { - # we have to do this ditemid->jitemid to make old code work, - # but this can probably go away at some point... - if (defined $_->{ditemid}) { - $_->{jitemid} = $_->{ditemid}; - } else { - $_->{ditemid} = $_->{jitemid}; - } - $memories{$_->{memid}} = $_; - } - - my @jids = map { $_->{journalid} } values %memories; - my $us = LJ::load_userids(@jids); - foreach my $mem (values %memories) { - next unless $mem->{journalid}; - $mem->{user} = $us->{$mem->{journalid}}->user; - } - - return \%memories; -} - -# <LJFUNC> -# name: LJ::Memories::get_by_id -# class: web -# des: Get memories given some memory ids. -# args: uuobj, memids -# des-uuobj: User id or user object to get memories for. -# des-memids: The rest of the memory ids. Array. (Pass them in as individual parameters...) -# returns: Hashref of memories with keys being memid; undef on error. -# </LJFUNC> -# sub get_by_id { -# my $u = shift; -# return {} unless @_; # make sure they gave us some ids -# -# # pass to getter to get by id -# return LJ::Memories::_memory_getter($u, { byid => [ map { $_+0 } @_ ] }); -# } - -# <LJFUNC> -# name: LJ::Memories::get_by_ditemid -# class: web -# des: Get memory for a given journal entry. -# args: uuobj, journalid, ditemid -# des-uuobj: User id or user object to get memories for. -# des-journalid: Userid for journal entry is in. -# des-ditemid: Display itemid of entry. -# returns: Hashref of individual memory. -# </LJFUNC> -sub get_by_ditemid { - my ($u, $jid, $ditemid) = @_; - $jid += 0; - $ditemid += 0; - return undef unless $ditemid; # _memory_getter checks $u and $jid isn't necessary - # because this might be an old-style memory - - # pass to getter with appropriate options - my $memhash = LJ::Memories::_memory_getter($u, { byditemid => $ditemid, journalid => $jid }); - return undef unless %{$memhash || {}}; - return [ values %$memhash ]->[0]; # ugly -} - -# <LJFUNC> -# name: LJ::Memories::get_by_user -# class: web -# des: Get memories given a user. -# args: uuobj -# des-uuobj: User id or user object to get memories for. -# returns: Hashref of memories with keys being memid; undef on error. -# </LJFUNC> -# sub get_by_user { -# # simply passes through to _memory_getter -# return LJ::Memories::_memory_getter(@_); -# } - -# <LJFUNC> -# name: LJ::Memories::get_by_keyword -# class: web -# des: Get memories given a user and a keyword/keyword id. -# args: uuobj, kwoid, opts -# des-uuobj: User id or user object to get memories for. -# des-kwoid: Keyword (string) or keyword id (number) to get memories for. -# des-opts: Hashref of extra options to pass through to memory getter. Suggested options -# are filter and security for limiting the memories returned. -# returns: Hashref of memories with keys being memid; undef on error. -# </LJFUNC> -sub get_by_keyword { - my ($u, $kwoid, $opts) = @_; - $u = LJ::want_user($u); - my $kwid = $kwoid+0; - my $kw = defined $kwoid && !$kwid ? $kwoid : undef; - return undef unless $u && ($kwid || defined $kw); - - my $memids; - my $dbcr = LJ::get_cluster_reader( $u ); - return undef unless $dbcr; - - # get keyword id if we don't have it - if ( defined $kw ) { - $kwid = $dbcr->selectrow_array( 'SELECT kwid FROM userkeywords WHERE userid = ? AND keyword = ?', - undef, $u->userid, $kw ) + 0; - } - return undef unless $kwid; - - # now get the actual memory ids - $memids = $dbcr->selectcol_arrayref( 'SELECT memid FROM memkeyword2 WHERE userid = ? AND kwid = ?', - undef, $u->{userid}, $kwid ); - return undef if $dbcr->err; - - # return - $memids = [] unless defined($memids); - my $memories = @$memids > 0 ? LJ::Memories::_memory_getter($u, {%{$opts || {}}, byid => $memids }) : {}; - return $memories; -} - -# <LJFUNC> -# name: LJ::Memories::get_keywords -# class: -# des: Retrieves keyword/keyids without big joins, returns a hashref. -# args: uobj -# des-uobj: User object to get keyword pairs for. -# returns: Hashref; { keywordid => keyword } -# </LJFUNC> -sub get_keywords { - my $u = shift; - $u = LJ::want_user($u); - return undef unless $u; - - my $use_reader = 0; - my $memkey = [$u->{userid},"memkwid:$u->{userid}"]; - my $ret = LJ::MemCache::get($memkey); - return $ret if defined $ret; - $ret = {}; - - my $dbcm = LJ::get_cluster_def_reader( $u ); - unless ( $dbcm ) { - $use_reader = 1; - $dbcm = LJ::get_cluster_reader( $u ); - } - my $ids = $dbcm->selectcol_arrayref( 'SELECT DISTINCT kwid FROM memkeyword2 WHERE userid = ?', - undef, $u->userid ); - if ( @{$ids || []} ) { - my $in = join ",", @$ids; - my $rows = $dbcm->selectall_arrayref( 'SELECT kwid, keyword FROM userkeywords ' . - "WHERE userid = ? AND kwid IN ($in)", - undef, $u->userid ); - $ret->{$_->[0]} = $_->[1] foreach @{$rows || []}; - } - - my $expiration = $LJ::MEMCACHE_EXPIRATION{'memkwid'} || 86400; - LJ::MemCache::set($memkey, $ret, $expiration) unless $use_reader; - return $ret; -} - -# <LJFUNC> -# name: LJ::Memories::updated_keywords -# class: web -# des: Deletes memcached keyword data. -# args: uobj -# des-uobj: User object to clear memcached keywords for. -# returns: undef. -# </LJFUNC> -sub updated_keywords { - return clear_memcache(shift); -} - -# <LJFUNC> -# name: LJ::Memories::clear_memcache -# class: web -# des: Deletes memcached keyword data. -# args: uobj -# des-uobj: User object to clear memcached keywords for. -# returns: undef. -# </LJFUNC> -sub clear_memcache { - my $u = shift; - return unless ref $u; - my $userid = $u->{userid}; - - LJ::MemCache::delete([$userid, "memct:$userid"]); - - LJ::MemCache::delete([$userid, "memkwid:$userid"]); - - # Delete all memkwcnt entries - LJ::MemCache::delete([$userid, "memkwcnt:$userid:w:f"]); - LJ::MemCache::delete([$userid, "memkwcnt:$userid:w:v"]); - LJ::MemCache::delete([$userid, "memkwcnt:$userid:w:u"]); - LJ::MemCache::delete([$userid, "memkwcnt:$userid:t:f"]); - LJ::MemCache::delete([$userid, "memkwcnt:$userid:t:v"]); - LJ::MemCache::delete([$userid, "memkwcnt:$userid:t:u"]); - - return undef; -} - -1; diff -r 670a563d0295 -r b74e50684390 cgi-bin/modperl_subs.pl --- a/cgi-bin/modperl_subs.pl Thu Sep 22 12:46:51 2011 +0800 +++ b/cgi-bin/modperl_subs.pl Thu Sep 22 12:52:49 2011 +0800 @@ -87,7 +87,7 @@ use LJ::CleanHTML; use LJ::Talk; require "ljfeed.pl"; -require "ljmemories.pl"; +use LJ::Memories; require "ljmail.pl"; use LJ::Sysban; use LJ::Community; --------------------------------------------------------------------------------