* update bin/dumpsql.pl
The schematables database table was dropped in b3bf367e8d
, so
the section of the dumpsql.pl script that used to regenerate
the base-data.sql file doesn't do anything any more.
* new global table profile_services
maps new database list of profile services to legacy userprops
and incorporates additional data from the external_services
function in DW/Logic/ProfilePage.pm and from /manage/profile
* update DW/Logic/ProfilePage.pm to use data from profile_services table
* move data list into new module DW::External::ProfileServices
* Do I remember how to create a clustered table? Let's find out.
* new user method load_profile_accts
* new user method save_profile_accts
* allow adding more than one of the same type of site
* move text_trim up in the stack, use it for safer string truncation
* use confess if there are database errors
* bypass memcache when preparing to save
* rewrite the entire database layer nbd
This commit is contained in:
parent
0ea48b3557
commit
166efb8bb2
121
bin/dumpsql.pl
121
bin/dumpsql.pl
|
@ -21,125 +21,6 @@ BEGIN {
|
|||
|
||||
my $dbh = LJ::get_db_writer();
|
||||
|
||||
sub header_text {
|
||||
return <<"HEADER";
|
||||
# This file is automatically generated from MySQL by \$LJHOME/bin/dumpsql.pl
|
||||
# Don't submit a diff against a hand-modified file - dump and diff instead.
|
||||
|
||||
HEADER
|
||||
}
|
||||
|
||||
# what tables don't we want to export the auto_increment columns from
|
||||
# because they already have their own unique string, which is what matters
|
||||
my %skip_auto = (
|
||||
"priv_list" => "privcode",
|
||||
"supportcat" => "catkey",
|
||||
"ratelist" => "name",
|
||||
);
|
||||
|
||||
# get tables to export
|
||||
my %tables = ();
|
||||
my $sth = $dbh->prepare( "SELECT tablename, redist_mode, redist_where "
|
||||
. "FROM schematables WHERE redist_mode NOT IN ('off')" );
|
||||
$sth->execute;
|
||||
while ( my ( $table, $mode, $where ) = $sth->fetchrow_array ) {
|
||||
$tables{$table}->{'mode'} = $mode;
|
||||
$tables{$table}->{'where'} = $where;
|
||||
}
|
||||
|
||||
my %output; # {general|local} -> [ [ $alphasortkey, $SQL ]+ ]
|
||||
|
||||
# dump each table.
|
||||
foreach my $table ( sort keys %tables ) {
|
||||
next if $table =~ /^(user|talk|log)proplist$/;
|
||||
|
||||
my $where;
|
||||
if ( $tables{$table}->{'where'} ) {
|
||||
$where = "WHERE $tables{$table}->{'where'}";
|
||||
}
|
||||
|
||||
my $sth = $dbh->prepare("DESCRIBE $table");
|
||||
$sth->execute;
|
||||
my @cols = ();
|
||||
my $skip_auto = 0;
|
||||
while ( my $c = $sth->fetchrow_hashref ) {
|
||||
if ( $c->{'Extra'} =~ /auto_increment/ && $skip_auto{$table} ) {
|
||||
$skip_auto = 1;
|
||||
}
|
||||
else {
|
||||
push @cols, $c;
|
||||
}
|
||||
}
|
||||
|
||||
# DESCRIBE table can be different between developers
|
||||
@cols = sort { $a->{'Field'} cmp $b->{'Field'} } @cols;
|
||||
|
||||
my $cols = join( ", ", map { $_->{'Field'} } @cols );
|
||||
my $sth = $dbh->prepare("SELECT $cols FROM $table $where");
|
||||
$sth->execute;
|
||||
my $sql;
|
||||
while ( my @r = $sth->fetchrow_array ) {
|
||||
my %vals;
|
||||
my $i = 0;
|
||||
foreach ( map { $_->{'Field'} } @cols ) {
|
||||
$vals{$_} = $r[ $i++ ];
|
||||
}
|
||||
my $scope = "general";
|
||||
$scope = "local" if ( defined $vals{'scope'}
|
||||
&& $vals{'scope'} eq "local" );
|
||||
my $verb = "INSERT IGNORE";
|
||||
$verb = "REPLACE" if ( $tables{$table}->{'mode'} eq "replace"
|
||||
&& !$skip_auto );
|
||||
$sql = "$verb INTO $table ";
|
||||
$sql .= "($cols) ";
|
||||
$sql .= "VALUES (" . join( ", ", map { db_quote($_) } @r ) . ");\n";
|
||||
|
||||
my $uniqc = $skip_auto{$table};
|
||||
my $skey = $uniqc ? $vals{$uniqc} : $sql;
|
||||
push @{ $output{$scope} }, [ "$table.$skey.1", $sql ];
|
||||
|
||||
if ($skip_auto) {
|
||||
|
||||
# for all the *proplist tables, there might be new descriptions
|
||||
# or columns, but we can't do a REPLACE, because that'd mess
|
||||
# with their auto_increment ids, so we do insert ignore + update
|
||||
my $where = "$uniqc=" . db_quote( $vals{$uniqc} );
|
||||
delete $vals{$uniqc};
|
||||
$sql = "UPDATE $table SET ";
|
||||
$sql .= join( ",", map { "$_=" . db_quote( $vals{$_} ) } sort keys %vals );
|
||||
$sql .= " WHERE $where;\n";
|
||||
push @{ $output{$scope} }, [ "$table.$skey.2", $sql ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# don't use $dbh->quote because it's changed between versions
|
||||
# and developers sending patches can't generate concise patches
|
||||
# it used to not quote " in a single quoted string, but later it does.
|
||||
# so we'll implement the new way here.
|
||||
sub db_quote {
|
||||
my $s = shift;
|
||||
return "NULL" unless defined $s;
|
||||
$s =~ s/\\/\\\\/g;
|
||||
$s =~ s/\"/\\\"/g;
|
||||
$s =~ s/\'/\\\'/g;
|
||||
$s =~ s/\n/\\n/g;
|
||||
$s =~ s/\r/\\r/g;
|
||||
return "'$s'";
|
||||
}
|
||||
|
||||
foreach my $k ( keys %output ) {
|
||||
my $file = $k eq "general" ? "base-data.sql" : "base-data-local.sql";
|
||||
print "Dumping $file\n";
|
||||
my $ffile = "$ENV{'LJHOME'}/bin/upgrading/$file";
|
||||
open( F, ">$ffile" ) or die "Can't write to $ffile\n";
|
||||
print F header_text();
|
||||
foreach ( sort { $a->[0] cmp $b->[0] } @{ $output{$k} } ) {
|
||||
print F $_->[1];
|
||||
}
|
||||
close F;
|
||||
}
|
||||
|
||||
# dump proplists, etc
|
||||
print "Dumping proplists.dat\n";
|
||||
open( my $plg, ">$ENV{LJHOME}/bin/upgrading/proplists.dat" ) or die;
|
||||
|
@ -181,7 +62,7 @@ foreach my $table ( 'userproplist', 'talkproplist', 'logproplist', 'usermsgpropl
|
|||
# and dump mood info
|
||||
print "Dumping moods.dat\n";
|
||||
open( F, ">$ENV{'LJHOME'}/bin/upgrading/moods.dat" ) or die;
|
||||
$sth = $dbh->prepare("SELECT moodid, mood, parentmood FROM moods ORDER BY moodid");
|
||||
my $sth = $dbh->prepare("SELECT moodid, mood, parentmood FROM moods ORDER BY moodid");
|
||||
$sth->execute;
|
||||
while ( @_ = $sth->fetchrow_array ) {
|
||||
print F "MOOD @_\n";
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# This file is automatically generated from MySQL by $LJHOME/bin/dumpsql.pl
|
||||
# Don't submit a diff against a hand-modified file - dump and diff instead.
|
||||
# This file is no longer automatically generated by $LJHOME/bin/dumpsql.pl
|
||||
|
||||
REPLACE INTO codes (code, item, sortorder, type) VALUES ('#000000', 'Black', '210', 'color');
|
||||
REPLACE INTO codes (code, item, sortorder, type) VALUES ('#0000FF', 'Blue, Bright', '150', 'color');
|
||||
|
@ -260,6 +259,7 @@ REPLACE INTO codes (code, item, sortorder, type) VALUES ('th', 'Thueringen', '0'
|
|||
REPLACE INTO codes (code, item, sortorder, type) VALUES ('vi', 'Victoria', '0', 'stateau');
|
||||
REPLACE INTO codes (code, item, sortorder, type) VALUES ('wa', 'Western Australia', '0', 'stateau');
|
||||
REPLACE INTO codes (code, item, sortorder, type) VALUES ('yt', 'Yukon Territory', '0', 'stateca');
|
||||
|
||||
INSERT IGNORE INTO priv_list (des, is_public, privcode, privname, scope) VALUES ('Allows a user to grant or revoke privileges to/from other users. arg=Unique privcode that the user has access to grant/deny for, or \"*\" for all privileges.', '0', 'admin', 'Administer privileges', 'general');
|
||||
UPDATE priv_list SET des='Allows a user to grant or revoke privileges to/from other users. arg=Unique privcode that the user has access to grant/deny for, or \"*\" for all privileges.',is_public='0',privname='Administer privileges',scope='general' WHERE privcode='admin';
|
||||
INSERT IGNORE INTO priv_list (des, is_public, privcode, privname, scope) VALUES ('Allows a user to view information that isn\'t otherwise available. All use is logged. arg=Arg=\"*\": can view everything, Arg=\"suspended\": can view public posts in a suspended journal, Arg=\"userlog\": can see userlog data.', '0', 'canview', 'View All Entries', 'general');
|
||||
|
@ -320,6 +320,7 @@ INSERT IGNORE INTO priv_list (des, is_public, privcode, privname, scope) VALUES
|
|||
UPDATE priv_list SET des='Allows a user to edit site text in a given language. arg=Unique language code, optionally appended by |domainid.domaincode',is_public='1',privname='Translate/Update Text',scope='general' WHERE privcode='translate';
|
||||
INSERT IGNORE INTO priv_list (des, is_public, privcode, privname, scope) VALUES ('Allows a user to add/edit vgifts. arg=Tag in case restricting priv to a particular category is needed, or "*" for all tags.', '1', 'vgifts', 'Virtual Gifts', 'general');
|
||||
UPDATE priv_list SET des='Allows a user to add/edit vgifts. arg=Tag in case restricting priv to a particular category is needed, or "*" for all tags.',is_public='1',privname='Virtual Gifts',scope='general' WHERE privcode='vgifts';
|
||||
|
||||
INSERT IGNORE INTO ratelist (des, name) VALUES ('Logged when a user adds someone to their Friends list', 'addfriend');
|
||||
UPDATE ratelist SET des='Logged when a user adds someone to their Friends list' WHERE name='addfriend';
|
||||
INSERT IGNORE INTO ratelist (des, name) VALUES ('Logged when a user creates a community.', 'commcreate');
|
||||
|
@ -336,5 +337,59 @@ INSERT IGNORE INTO ratelist (des, name) VALUES ('Logged when a users sends a mes
|
|||
UPDATE ratelist SET des='Logged when a users sends a message via Tell A Friend' WHERE name='tellafriend';
|
||||
INSERT IGNORE INTO ratelist (des, name) VALUES ('Logged when a users sends a message to another user', 'usermessage');
|
||||
UPDATE ratelist SET des='Logged when a users sends a message to another user' WHERE name='usermessage';
|
||||
|
||||
INSERT IGNORE INTO supportcat (allow_screened, basepoints, catkey, catname, hide_helpers, is_selectable, no_autoreply, public_help, public_read, replyaddress, scope, sortorder, user_closeable) VALUES ('1', '1', 'general', 'General/Unknown', '0', '1', '0', '0', '1', NULL, 'general', '2', '1');
|
||||
UPDATE supportcat SET allow_screened='1',basepoints='1',catname='General/Unknown',hide_helpers='0',is_selectable='1',no_autoreply='0',public_help='0',public_read='1',replyaddress=NULL,scope='general',sortorder='2',user_closeable='1' WHERE catkey='general';
|
||||
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('ao3', 'ao3', 'ao3.png', 'profile.service.ao3', '//archiveofourown.org/users/%s', 40);
|
||||
UPDATE profile_services SET userprop='ao3', imgfile='ao3.png', title_ml='profile.service.ao3', url_format='//archiveofourown.org/users/%s', maxlen=40 WHERE name='ao3';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('deviantart', 'deviantart', 'deviantart.png', 'profile.service.deviantart', '//%s.deviantart.com', 20);
|
||||
UPDATE profile_services SET userprop='deviantart', imgfile='deviantart.png', title_ml='profile.service.deviantart', url_format='//%s.deviantart.com', maxlen=20 WHERE name='deviantart';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('diigo', 'diigo', 'diigo.png', 'profile.service.diigo', '//www.diigo.com/user/%s', 16);
|
||||
UPDATE profile_services SET userprop='diigo', imgfile='diigo.png', title_ml='profile.service.diigo', url_format='//www.diigo.com/user/%s', maxlen=16 WHERE name='diigo';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('discord', 'discord', 'discord.png', 'profile.service.discord', NULL, 40);
|
||||
UPDATE profile_services SET userprop='discord', imgfile='discord.png', title_ml='profile.service.discord', url_format=NULL, maxlen=40 WHERE name='discord';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('etsy', 'etsy', 'etsy.png', 'profile.service.etsy', '//www.etsy.com/people/%s', 20);
|
||||
UPDATE profile_services SET userprop='etsy', imgfile='etsy.png', title_ml='profile.service.etsy', url_format='//www.etsy.com/people/%s', maxlen=20 WHERE name='etsy';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('ffnet', 'ffnet', 'ffnet.png', 'profile.service.ffnet', '//www.fanfiction.net/~%s', 30);
|
||||
UPDATE profile_services SET userprop='ffnet', imgfile='ffnet.png', title_ml='profile.service.ffnet', url_format='//www.fanfiction.net/~%s', maxlen=30 WHERE name='ffnet';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('github', 'github', 'github.png', 'profile.service.github', '//github.com/%s', 39);
|
||||
UPDATE profile_services SET userprop='github', imgfile='github.png', title_ml='profile.service.github', url_format='//github.com/%s', maxlen=39 WHERE name='github';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('google_chat', 'google_talk', 'google_hangouts.png', 'profile.service.hangouts', NULL, 60);
|
||||
UPDATE profile_services SET userprop='google_talk', imgfile='google_hangouts.png', title_ml='profile.service.hangouts', url_format=NULL, maxlen=60 WHERE name='google_chat';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('icq', 'icq', 'icq.gif', 'profile.service.icq', '//wwp.icq.com/%s', 12);
|
||||
UPDATE profile_services SET userprop='icq', imgfile='icq.gif', title_ml='profile.service.icq', url_format='//wwp.icq.com/%s', maxlen=12 WHERE name='icq';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('insanejournal', 'insanejournal', 'insanejournal.png', 'profile.service.insanejournal', '//%s.insanejournal.com', 15);
|
||||
UPDATE profile_services SET userprop='insanejournal', imgfile='insanejournal.png', title_ml='profile.service.insanejournal', url_format='//%s.insanejournal.com', maxlen=15 WHERE name='insanejournal';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('instagram', 'instagram', 'instagram.png', 'profile.service.instagram', '//www.instagram.com/%s', 30);
|
||||
UPDATE profile_services SET userprop='instagram', imgfile='instagram.png', title_ml='profile.service.instagram', url_format='//www.instagram.com/%s', maxlen=30 WHERE name='instagram';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('jabber', 'jabber', 'jabber.gif', 'profile.service.jabber', NULL, 60);
|
||||
UPDATE profile_services SET userprop='jabber', imgfile='jabber.gif', title_ml='profile.service.jabber', url_format=NULL, maxlen=60 WHERE name='jabber';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('lastfm', 'last_fm_user', 'lastfm.gif', 'profile.service.lastfm', '//www.last.fm/user/%s', 255);
|
||||
UPDATE profile_services SET userprop='last_fm_user', imgfile='lastfm.gif', title_ml='profile.service.lastfm', url_format='//www.last.fm/user/%s', maxlen=255 WHERE name='lastfm';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('livejournal', 'livejournal', 'livejournal.gif', 'profile.service.livejournal', '//%s.livejournal.com', 30);
|
||||
UPDATE profile_services SET userprop='livejournal', imgfile='livejournal.gif', title_ml='profile.service.livejournal', url_format='//%s.livejournal.com', maxlen=30 WHERE name='livejournal';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('medium', 'medium', 'medium.png', 'profile.service.medium', '//medium.com/@%s/latest', 25);
|
||||
UPDATE profile_services SET userprop='medium', imgfile='medium.png', title_ml='profile.service.medium', url_format='//medium.com/@%s/latest', maxlen=25 WHERE name='medium';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('patreon', 'patreon', 'patreon.png', 'profile.service.patreon', '//www.patreon.com/%s', 255);
|
||||
UPDATE profile_services SET userprop='patreon', imgfile='patreon.png', title_ml='profile.service.patreon', url_format='//www.patreon.com/%s', maxlen=255 WHERE name='patreon';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('pillowfort', 'pillowfort', 'pillowfort.png', 'profile.service.pillowfort', '//www.pillowfort.social/%s', 255);
|
||||
UPDATE profile_services SET userprop='pillowfort', imgfile='pillowfort.png', title_ml='profile.service.pillowfort', url_format='//www.pillowfort.social/%s', maxlen=255 WHERE name='pillowfort';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('pinboard', 'pinboard', 'pinboard.png', 'profile.service.pinboard', '//pinboard.in/u:%s', 30);
|
||||
UPDATE profile_services SET userprop='pinboard', imgfile='pinboard.png', title_ml='profile.service.pinboard', url_format='//pinboard.in/u:%s', maxlen=30 WHERE name='pinboard';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('pinterest', 'pinterest', 'pinterest.png', 'profile.service.pinterest', '//www.pinterest.com/%s', 30);
|
||||
UPDATE profile_services SET userprop='pinterest', imgfile='pinterest.png', title_ml='profile.service.pinterest', url_format='//www.pinterest.com/%s', maxlen=30 WHERE name='pinterest';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('plurk', 'plurk', 'plurk.png', 'profile.service.plurk', '//www.plurk.com/%s', 255);
|
||||
UPDATE profile_services SET userprop='plurk', imgfile='plurk.png', title_ml='profile.service.plurk', url_format='//www.plurk.com/%s', maxlen=255 WHERE name='plurk';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('ravelry', 'ravelry', 'ravelry.png', 'profile.service.ravelry', '//www.ravelry.com/people/%s', 40);
|
||||
UPDATE profile_services SET userprop='ravelry', imgfile='ravelry.png', title_ml='profile.service.ravelry', url_format='//www.ravelry.com/people/%s', maxlen=40 WHERE name='ravelry';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('reddit', 'reddit', 'reddit.png', 'profile.service.reddit', '//www.reddit.com/user/%s', 20);
|
||||
UPDATE profile_services SET userprop='reddit', imgfile='reddit.png', title_ml='profile.service.reddit', url_format='//www.reddit.com/user/%s', maxlen=20 WHERE name='reddit';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('skype', 'skype', 'skype.gif', 'profile.service.skype', NULL, 40);
|
||||
UPDATE profile_services SET userprop='skype', imgfile='skype.gif', title_ml='profile.service.skype', url_format=NULL, maxlen=40 WHERE name='skype';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('tumblr', 'tumblr', 'tumblr.png', 'profile.service.tumblr', '//%s.tumblr.com', 255);
|
||||
UPDATE profile_services SET userprop='tumblr', imgfile='tumblr.png', title_ml='profile.service.tumblr', url_format='//%s.tumblr.com', maxlen=255 WHERE name='tumblr';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('twitter', 'twitter', 'twitter_bird.png', 'profile.service.twitter', '//www.twitter.com/%s', 40);
|
||||
UPDATE profile_services SET userprop='twitter', imgfile='twitter_bird.png', title_ml='profile.service.twitter', url_format='//www.twitter.com/%s', maxlen=40 WHERE name='twitter';
|
||||
INSERT IGNORE INTO profile_services (name, userprop, imgfile, title_ml, url_format, maxlen) VALUES ('wattpad', 'wattpad', 'wattpad.png', 'profile.service.wattpad', '//www.wattpad.com/user/%s', 20);
|
||||
UPDATE profile_services SET userprop='wattpad', imgfile='wattpad.png', title_ml='profile.service.wattpad', url_format='//www.wattpad.com/user/%s', maxlen=20 WHERE name='wattpad';
|
||||
|
|
|
@ -2297,6 +2297,58 @@ post.security.private.p=The entry is only visible to you (private).
|
|||
|
||||
post.security.public=The entry is visible to everyone (public).
|
||||
|
||||
profile.service.ao3=Archive of Our Own
|
||||
|
||||
profile.service.deviantart=DeviantArt
|
||||
|
||||
profile.service.diigo=Diigo
|
||||
|
||||
profile.service.discord=Discord
|
||||
|
||||
profile.service.etsy=Etsy
|
||||
|
||||
profile.service.ffnet=FanFiction.net
|
||||
|
||||
profile.service.github=GitHub
|
||||
|
||||
profile.service.hangouts=Google Chat
|
||||
|
||||
profile.service.icq=ICQ
|
||||
|
||||
profile.service.insanejournal=InsaneJournal
|
||||
|
||||
profile.service.instagram=Instagram
|
||||
|
||||
profile.service.jabber=Jabber
|
||||
|
||||
profile.service.lastfm=Last.fm
|
||||
|
||||
profile.service.livejournal=LiveJournal
|
||||
|
||||
profile.service.medium=Medium
|
||||
|
||||
profile.service.patreon=Patreon
|
||||
|
||||
profile.service.pillowfort=Pillowfort
|
||||
|
||||
profile.service.pinboard=Pinboard
|
||||
|
||||
profile.service.pinterest=Pinterest
|
||||
|
||||
profile.service.plurk=Plurk
|
||||
|
||||
profile.service.ravelry=Ravelry
|
||||
|
||||
profile.service.reddit=Reddit
|
||||
|
||||
profile.service.skype=Skype
|
||||
|
||||
profile.service.tumblr=Tumblr
|
||||
|
||||
profile.service.twitter=Twitter
|
||||
|
||||
profile.service.wattpad=Wattpad
|
||||
|
||||
protocol.bad_password=Your password is too easy to guess. We strongly recommend you change it, otherwise you risk having your journal hijacked. Visit [[siteroot]]/changepassword to change your password.
|
||||
|
||||
protocol.mail_bouncing=You are currently using a bad email address. All mail we try to send you is bouncing. We require a valid email address for continued use. Visit the support area for more information.
|
||||
|
|
|
@ -20,7 +20,7 @@ userproplist.ao3:
|
|||
des: AO3 user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Archive of Our Own user account name
|
||||
prettyname: Archive of Our Own user account name (legacy value)
|
||||
|
||||
userproplist.betafeatures_list:
|
||||
cldversion: 4
|
||||
|
@ -196,7 +196,7 @@ userproplist.deviantart:
|
|||
des: DeviantArt user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: DeviantArt user account name
|
||||
prettyname: DeviantArt user account name (legacy value)
|
||||
|
||||
userproplist.disable_auto_formatting:
|
||||
cldversion: 4
|
||||
|
@ -220,7 +220,7 @@ userproplist.discord:
|
|||
des: Discord user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Discord user account name
|
||||
prettyname: Discord user account name (legacy value)
|
||||
|
||||
userproplist.displaydate_check:
|
||||
cldversion: 4
|
||||
|
@ -380,7 +380,7 @@ userproplist.etsy:
|
|||
des: Etsy user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Etsy user account name
|
||||
prettyname: Etsy user account name (legacy value)
|
||||
|
||||
userproplist.diigo:
|
||||
cldversion: 4
|
||||
|
@ -388,7 +388,7 @@ userproplist.diigo:
|
|||
des: Diigo user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Diigo user account name
|
||||
prettyname: Diigo user account name (legacy value)
|
||||
|
||||
userproplist.exclude_from_own_stats:
|
||||
cldversion: 4
|
||||
|
@ -404,7 +404,7 @@ userproplist.ffnet:
|
|||
des: FanFiction.net user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: FanFiction.net user account name
|
||||
prettyname: FanFiction.net user account name (legacy value)
|
||||
|
||||
userproplist.friendspagetitle:
|
||||
cldversion: 4
|
||||
|
@ -444,7 +444,7 @@ userproplist.github:
|
|||
des: GitHub user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: GitHub user account name
|
||||
prettyname: GitHub user account name (legacy value)
|
||||
|
||||
userproplist.google_talk:
|
||||
cldversion: 0
|
||||
|
@ -452,7 +452,7 @@ userproplist.google_talk:
|
|||
des: Google Chat Service address
|
||||
indexed: 1
|
||||
multihomed: 1
|
||||
prettyname: Google Chat Address
|
||||
prettyname: Google Chat Address (legacy value)
|
||||
|
||||
userproplist.google_analytics:
|
||||
cldversion: 4
|
||||
|
@ -516,7 +516,7 @@ userproplist.icq:
|
|||
des: ICQ Number
|
||||
indexed: 1
|
||||
multihomed: 1
|
||||
prettyname: ICQ
|
||||
prettyname: ICQ (legacy value)
|
||||
|
||||
userproplist.import_job:
|
||||
cldversion: 4
|
||||
|
@ -556,7 +556,7 @@ userproplist.insanejournal:
|
|||
des: InsaneJournal account name for user profile
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: InsaneJournal Account
|
||||
prettyname: InsaneJournal Account (legacy value)
|
||||
|
||||
userproplist.instagram:
|
||||
cldversion: 4
|
||||
|
@ -564,7 +564,7 @@ userproplist.instagram:
|
|||
des: Instagram account name for user profile
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Instagram Account
|
||||
prettyname: Instagram Account (legacy value)
|
||||
|
||||
userproplist.jabber:
|
||||
cldversion: 0
|
||||
|
@ -572,7 +572,7 @@ userproplist.jabber:
|
|||
des: Jabber address (username@server)
|
||||
indexed: 1
|
||||
multihomed: 1
|
||||
prettyname: Jabber Address
|
||||
prettyname: Jabber Address (legacy value)
|
||||
|
||||
userproplist.journalsubtitle:
|
||||
cldversion: 4
|
||||
|
@ -620,7 +620,7 @@ userproplist.last_fm_user:
|
|||
des: LastFM user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: LastFM user account name
|
||||
prettyname: LastFM user account name (legacy value)
|
||||
|
||||
userproplist.livejournal:
|
||||
cldversion: 4
|
||||
|
@ -628,7 +628,7 @@ userproplist.livejournal:
|
|||
des: LiveJournal user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: LiveJournal user account name
|
||||
prettyname: LiveJournal user account name (legacy value)
|
||||
|
||||
userproplist.latest_optout:
|
||||
cldversion: 4
|
||||
|
@ -644,7 +644,7 @@ userproplist.medium:
|
|||
des: Medium user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Medium user account name
|
||||
prettyname: Medium user account name (legacy value)
|
||||
|
||||
userproplist.moderated:
|
||||
cldversion: 4
|
||||
|
@ -1116,7 +1116,7 @@ userproplist.patreon:
|
|||
des: Patreon user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Patreon user account name
|
||||
prettyname: Patreon user account name (legacy value)
|
||||
|
||||
userproplist.pillowfort:
|
||||
cldversion: 4
|
||||
|
@ -1124,7 +1124,7 @@ userproplist.pillowfort:
|
|||
des: Pillowfort user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Pillowfort user account name
|
||||
prettyname: Pillowfort user account name (legacy value)
|
||||
|
||||
userproplist.pinboard:
|
||||
cldversion: 4
|
||||
|
@ -1132,7 +1132,7 @@ userproplist.pinboard:
|
|||
des: Pinboard user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Pinboard user account name
|
||||
prettyname: Pinboard user account name (legacy value)
|
||||
|
||||
userproplist.pinterest:
|
||||
cldversion: 4
|
||||
|
@ -1140,7 +1140,7 @@ userproplist.pinterest:
|
|||
des: Pinterest user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Pinterest user account name
|
||||
prettyname: Pinterest user account name (legacy value)
|
||||
|
||||
userproplist.plurk:
|
||||
cldversion: 4
|
||||
|
@ -1148,7 +1148,7 @@ userproplist.plurk:
|
|||
des: Plurk user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Plurk user account name
|
||||
prettyname: Plurk user account name (legacy value)
|
||||
|
||||
userproplist.posting_guidelines_entry:
|
||||
cldversion: 4
|
||||
|
@ -1180,7 +1180,7 @@ userproplist.ravelry:
|
|||
des: Ravelry user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Ravelry user account name
|
||||
prettyname: Ravelry user account name (legacy value)
|
||||
|
||||
userproplist.reddit:
|
||||
cldversion: 4
|
||||
|
@ -1188,7 +1188,7 @@ userproplist.reddit:
|
|||
des: Reddit user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Reddit user account name
|
||||
prettyname: Reddit user account name (legacy value)
|
||||
|
||||
userproplist.renamedto:
|
||||
cldversion: 4
|
||||
|
@ -1292,7 +1292,7 @@ userproplist.skype:
|
|||
des: Skype Account ID
|
||||
indexed: 1
|
||||
multihomed: 1
|
||||
prettyname: Skype ID
|
||||
prettyname: Skype ID (legacy value)
|
||||
|
||||
userproplist.state:
|
||||
cldversion: 4
|
||||
|
@ -1356,7 +1356,7 @@ userproplist.tumblr:
|
|||
des: Tumblr user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Tumblr user account name
|
||||
prettyname: Tumblr user account name (legacy value)
|
||||
|
||||
userproplist.twitter:
|
||||
cldversion: 4
|
||||
|
@ -1364,7 +1364,7 @@ userproplist.twitter:
|
|||
des: Twitter user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Twitter user account name
|
||||
prettyname: Twitter user account name (legacy value)
|
||||
|
||||
userproplist.url:
|
||||
cldversion: 4
|
||||
|
@ -1404,7 +1404,7 @@ userproplist.wattpad:
|
|||
des: Wattpad user account name
|
||||
indexed: 0
|
||||
multihomed: 0
|
||||
prettyname: Wattpad user account name
|
||||
prettyname: Wattpad user account name (legacy value)
|
||||
|
||||
userproplist.who_invited:
|
||||
cldversion: 4
|
||||
|
|
|
@ -3120,6 +3120,34 @@ CREATE TABLE `key_prop_list` (
|
|||
)
|
||||
EOC
|
||||
|
||||
register_tablecreate( "profile_services", <<'EOC');
|
||||
CREATE TABLE profile_services (
|
||||
service_id MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(40) NOT NULL,
|
||||
userprop VARCHAR(40),
|
||||
imgfile VARCHAR(40) NOT NULL,
|
||||
title_ml VARCHAR(80) NOT NULL,
|
||||
url_format VARCHAR(255),
|
||||
maxlen TINYINT(3) UNSIGNED NOT NULL,
|
||||
|
||||
PRIMARY KEY (service_id),
|
||||
UNIQUE KEY (name)
|
||||
)
|
||||
EOC
|
||||
|
||||
# clustered
|
||||
register_tablecreate( "user_profile_accts", <<'EOC');
|
||||
CREATE TABLE user_profile_accts (
|
||||
userid INT(10) UNSIGNED NOT NULL,
|
||||
account_id MEDIUMINT(8) UNSIGNED NOT NULL,
|
||||
service_id MEDIUMINT(8) UNSIGNED NOT NULL,
|
||||
value VARCHAR(255) NOT NULL,
|
||||
|
||||
PRIMARY KEY (userid, account_id),
|
||||
INDEX (value)
|
||||
)
|
||||
EOC
|
||||
|
||||
# NOTE: new table declarations go ABOVE here ;)
|
||||
|
||||
### changes
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# DW::External::ProfileServices
|
||||
#
|
||||
# Information on external services referenced on profile pages.
|
||||
#
|
||||
# Authors:
|
||||
# Jen Griffin <kareila@livejournal.com>
|
||||
#
|
||||
# Copyright (c) 2009-2023 by Dreamwidth Studios, LLC.
|
||||
#
|
||||
# This program is free software; you may redistribute it and/or modify it under
|
||||
# the same terms as Perl itself. For a copy of the license, please reference
|
||||
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
||||
#
|
||||
|
||||
package DW::External::ProfileServices;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Carp qw/ confess /;
|
||||
|
||||
sub list {
|
||||
my ( $class, %opts ) = @_;
|
||||
|
||||
# load services from memcache
|
||||
my $memkey = 'profile_services';
|
||||
my $services = LJ::MemCache::get($memkey);
|
||||
return $services if $services;
|
||||
|
||||
# load services from database and add to memcache (expiring hourly)
|
||||
my $dbr = LJ::get_db_reader();
|
||||
my $data = $dbr->selectall_hashref(
|
||||
"SELECT service_id, name, userprop, imgfile, title_ml,"
|
||||
. " url_format, maxlen FROM profile_services",
|
||||
"name"
|
||||
);
|
||||
confess $dbr->errstr if $dbr->err;
|
||||
|
||||
$services = [ map { $data->{$_} } sort keys %$data ];
|
||||
LJ::MemCache::set( $memkey, $services, 3600 );
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
sub userprops {
|
||||
my ( $class, %opts ) = @_;
|
||||
|
||||
my $services = $class->list;
|
||||
my @userprops;
|
||||
|
||||
foreach my $site (@$services) {
|
||||
push @userprops, $site->{userprop} if defined $site->{userprop};
|
||||
}
|
||||
|
||||
return \@userprops;
|
||||
}
|
||||
|
||||
### user methods
|
||||
|
||||
sub load_profile_accts {
|
||||
my ( $u, %args ) = @_;
|
||||
$u = LJ::want_user($u) or confess 'invalid user object';
|
||||
my $uid = $u->userid;
|
||||
|
||||
# load accounts from memcache
|
||||
my $memkey = [ $uid, "profile_accts:$uid" ];
|
||||
my $accounts = LJ::MemCache::get($memkey);
|
||||
return $accounts if $accounts && !$args{force_db};
|
||||
|
||||
$accounts = {};
|
||||
|
||||
# load accounts from database and add to memcache (no expiration)
|
||||
my $dbcr = LJ::get_cluster_reader($u) or die;
|
||||
my $data = $dbcr->selectall_arrayref(
|
||||
"SELECT service_id, account_id, value FROM user_profile_accts"
|
||||
. " WHERE userid=? ORDER BY value",
|
||||
{ Slice => {} },
|
||||
$uid
|
||||
);
|
||||
confess $dbcr->errstr if $dbcr->err;
|
||||
|
||||
foreach my $acct (@$data) {
|
||||
my $s_id = $acct->{service_id};
|
||||
$accounts->{$s_id} //= [];
|
||||
push @{ $accounts->{$s_id} }, [ $acct->{account_id}, $acct->{value} ];
|
||||
}
|
||||
|
||||
LJ::MemCache::set( $memkey, $accounts );
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
*LJ::User::load_profile_accts = \&load_profile_accts;
|
||||
*DW::User::load_profile_accts = \&load_profile_accts;
|
||||
|
||||
sub save_profile_accts {
|
||||
my ( $u, $new_accts, %opts ) = @_;
|
||||
$u = LJ::want_user($u) or confess 'invalid user object';
|
||||
my $old_accts = $u->load_profile_accts( force_db => 1 );
|
||||
|
||||
# expire memcache after updating db
|
||||
my $uid = $u->userid;
|
||||
my $memkey = [ $uid, "profile_accts:$uid" ];
|
||||
|
||||
return unless $u->writer;
|
||||
|
||||
# if %$old_accts is empty, we need to clear out the user's legacy userprops
|
||||
# to avoid an edge case where if a user clears out all of their accounts
|
||||
# later, the old userprop values will suddenly reappear on their profile
|
||||
unless (%$old_accts) {
|
||||
my $userprops = DW::External::ProfileServices->userprops;
|
||||
my %prop = map { $_ => '' } @$userprops;
|
||||
$u->set_prop( \%prop, undef, { skip_db => 1 } );
|
||||
}
|
||||
|
||||
while ( my ( $s_id, $multival ) = each %$new_accts ) {
|
||||
foreach my $val (@$multival) {
|
||||
if ( ref $val && $val->[1] ) {
|
||||
|
||||
# update the value of the existing row
|
||||
$u->do(
|
||||
"UPDATE user_profile_accts SET value = ? WHERE account_id = ? AND userid = ?",
|
||||
undef, $val->[1], $val->[0], $uid );
|
||||
}
|
||||
elsif ( ref $val ) {
|
||||
|
||||
# delete the existing row
|
||||
$u->do( "DELETE FROM user_profile_accts WHERE account_id = ? AND userid = ?",
|
||||
undef, $val->[0], $uid );
|
||||
}
|
||||
else {
|
||||
# new addition or legacy upgrade
|
||||
my $a_id = LJ::alloc_user_counter( $u, 'P' );
|
||||
$u->do(
|
||||
"INSERT INTO user_profile_accts (userid, account_id, service_id, value)"
|
||||
. " VALUES (?,?,?,?)",
|
||||
undef, $uid, $a_id, $s_id, $val
|
||||
);
|
||||
}
|
||||
confess $u->errstr if $u->err;
|
||||
}
|
||||
}
|
||||
|
||||
LJ::MemCache::delete($memkey);
|
||||
|
||||
return 1;
|
||||
}
|
||||
*LJ::User::save_profile_accts = \&save_profile_accts;
|
||||
*DW::User::save_profile_accts = \&save_profile_accts;
|
||||
|
||||
1;
|
|
@ -21,6 +21,7 @@ package DW::Logic::ProfilePage;
|
|||
use strict;
|
||||
use DW::Countries;
|
||||
use DW::Logic::UserLinkBar;
|
||||
use DW::External::ProfileServices;
|
||||
|
||||
# returns a new profile page object
|
||||
sub profile_page {
|
||||
|
@ -770,318 +771,48 @@ sub external_services {
|
|||
|
||||
return [] unless $u->is_personal && ( $u->share_contactinfo($remote) || $self->{viewall} );
|
||||
|
||||
# this has gotten big enough that we should improve database efficiency
|
||||
$u->preload_props(
|
||||
qw/
|
||||
ao3 deviantart diigo discord etsy ffnet github google_talk
|
||||
icq insanejournal instagram jabber last_fm_user livejournal
|
||||
medium patreon pillowfort pinboard pinterest plurk ravelry
|
||||
reddit skype tumblr twitter wattpad
|
||||
/
|
||||
);
|
||||
my $info = sub {
|
||||
my ( $acct, $site ) = @_;
|
||||
|
||||
if ( my $ao3 = $u->prop('ao3') ) {
|
||||
my $url = sprintf( "//archiveofourown.org/users/%s", LJ::eurl($ao3) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'ao3',
|
||||
text => LJ::ehtml($ao3),
|
||||
my $url =
|
||||
defined $site->{url_format}
|
||||
? sprintf( $site->{url_format}, LJ::eurl($acct) )
|
||||
: undef;
|
||||
|
||||
return {
|
||||
type => $site->{name},
|
||||
text => LJ::ehtml($acct),
|
||||
url => $url,
|
||||
image => 'ao3.png',
|
||||
title_ml => '.service.ao3',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $deviantart = $u->prop('deviantart') ) {
|
||||
my $url = sprintf( "//%s.deviantart.com", LJ::eurl($deviantart) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'deviantart',
|
||||
text => LJ::ehtml($deviantart),
|
||||
url => $url,
|
||||
image => 'deviantart.png',
|
||||
title_ml => '.service.deviantart',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $diigo = $u->prop('diigo') ) {
|
||||
my $url = sprintf( "//www.diigo.com/user/%s", LJ::eurl($diigo) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'diigo',
|
||||
text => LJ::ehtml($diigo),
|
||||
url => $url,
|
||||
image => 'diigo.png',
|
||||
title_ml => '.service.diigo',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $discord = $u->prop('discord') ) {
|
||||
push @ret,
|
||||
{
|
||||
type => 'discord',
|
||||
text => LJ::ehtml($discord),
|
||||
image => 'discord.png',
|
||||
title_ml => '.service.discord',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $etsy = $u->prop('etsy') ) {
|
||||
my $url = sprintf( "//www.etsy.com/people/%s", LJ::eurl($etsy) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'etsy',
|
||||
text => LJ::ehtml($etsy),
|
||||
url => $url,
|
||||
image => 'etsy.png',
|
||||
title_ml => '.service.etsy',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $ffnet = $u->prop('ffnet') ) {
|
||||
my $url = sprintf( "//www.fanfiction.net/~%s", LJ::eurl($ffnet) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'ffnet',
|
||||
text => LJ::ehtml($ffnet),
|
||||
url => $url,
|
||||
image => 'ffnet.png',
|
||||
title_ml => '.service.ffnet',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $github = $u->prop('github') ) {
|
||||
my $url = sprintf( "//github.com/%s", LJ::eurl($github) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'github',
|
||||
text => LJ::ehtml($github),
|
||||
url => $url,
|
||||
image => 'github.png',
|
||||
title_ml => '.service.github',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $google = $u->prop('google_talk') ) {
|
||||
push @ret,
|
||||
{
|
||||
type => 'google',
|
||||
email => LJ::ehtml($google),
|
||||
image => 'google_hangouts.png',
|
||||
title_ml => '.service.hangouts',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $icq = $u->prop('icq') ) {
|
||||
my $url = sprintf( "//wwp.icq.com/%s", LJ::eurl($icq) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'icq',
|
||||
text => LJ::ehtml($icq),
|
||||
url => $url,
|
||||
image => 'icq.gif',
|
||||
title_ml => '.service.icq',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $insanejournal = $u->prop('insanejournal') ) {
|
||||
my $url = sprintf( "//%s.insanejournal.com", LJ::eurl($insanejournal) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'insanejournal',
|
||||
text => LJ::ehtml($insanejournal),
|
||||
url => $url,
|
||||
image => 'insanejournal.png',
|
||||
title_ml => '.service.insanejournal',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $instagram = $u->prop('instagram') ) {
|
||||
my $url = sprintf( "//www.instagram.com/%s", LJ::eurl($instagram) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'instagram',
|
||||
text => LJ::ehtml($instagram),
|
||||
url => $url,
|
||||
image => 'instagram.png',
|
||||
title_ml => '.service.instagram',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $jabber = $u->prop('jabber') ) {
|
||||
push @ret,
|
||||
{
|
||||
type => 'jabber',
|
||||
email => LJ::ehtml($jabber),
|
||||
image => 'jabber.gif',
|
||||
title_ml => '.service.jabber',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $lastfm = $u->prop('last_fm_user') ) {
|
||||
my $url = sprintf( "//www.last.fm/user/%s", LJ::eurl($lastfm) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'lastfm',
|
||||
text => LJ::ehtml($lastfm),
|
||||
url => $url,
|
||||
image => 'lastfm.gif',
|
||||
title_ml => '.service.lastfm',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $livejournal = $u->prop('livejournal') ) {
|
||||
my $url = sprintf( "//%s.livejournal.com", LJ::eurl($livejournal) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'livejournal',
|
||||
text => LJ::ehtml($livejournal),
|
||||
url => $url,
|
||||
image => 'livejournal.gif',
|
||||
title_ml => '.service.livejournal',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $medium = $u->prop('medium') ) {
|
||||
my $url = sprintf( "//medium.com/@%s/latest", LJ::eurl($medium) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'medium',
|
||||
text => LJ::ehtml($medium),
|
||||
url => $url,
|
||||
image => 'medium.png',
|
||||
title_ml => '.service.medium',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $patreon = $u->prop('patreon') ) {
|
||||
my $url = sprintf( "//www.patreon.com/%s", LJ::eurl($patreon) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'patreon',
|
||||
text => LJ::ehtml($patreon),
|
||||
url => $url,
|
||||
image => 'patreon.png',
|
||||
title_ml => '.service.patreon',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $pillowfort = $u->prop('pillowfort') ) {
|
||||
my $url = sprintf( "//www.pillowfort.social/%s", LJ::eurl($pillowfort) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'pillowfort',
|
||||
text => LJ::ehtml($pillowfort),
|
||||
url => $url,
|
||||
image => 'pillowfort.png',
|
||||
title_ml => '.service.pillowfort',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $pinboard = $u->prop('pinboard') ) {
|
||||
my $url = sprintf( "//pinboard.in/u:%s", LJ::eurl($pinboard) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'pinboard',
|
||||
text => LJ::ehtml($pinboard),
|
||||
url => $url,
|
||||
image => 'pinboard.png',
|
||||
title_ml => '.service.pinboard',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $pinterest = $u->prop('pinterest') ) {
|
||||
my $url = sprintf( "//www.pinterest.com/%s", LJ::eurl($pinterest) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'pinterest',
|
||||
text => LJ::ehtml($pinterest),
|
||||
url => $url,
|
||||
image => 'pinterest.png',
|
||||
title_ml => '.service.pinterest',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $plurk = $u->prop('plurk') ) {
|
||||
my $url = sprintf( "//www.plurk.com/%s", LJ::eurl($plurk) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'plurk',
|
||||
text => LJ::ehtml($plurk),
|
||||
url => $url,
|
||||
image => 'plurk.png',
|
||||
title_ml => '.service.plurk',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $ravelry = $u->prop('ravelry') ) {
|
||||
my $url = sprintf( "//www.ravelry.com/people/%s", LJ::eurl($ravelry) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'ravelry',
|
||||
text => LJ::ehtml($ravelry),
|
||||
url => $url,
|
||||
image => 'ravelry.png',
|
||||
title_ml => '.service.ravelry',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $reddit = $u->prop('reddit') ) {
|
||||
my $url = sprintf( "//www.reddit.com/user/%s", LJ::eurl($reddit) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'reddit',
|
||||
text => LJ::ehtml($reddit),
|
||||
url => $url,
|
||||
image => 'reddit.png',
|
||||
title_ml => '.service.reddit',
|
||||
};
|
||||
}
|
||||
|
||||
if ( my $skype = $u->prop('skype') ) {
|
||||
my $service = {
|
||||
type => 'skype',
|
||||
email => LJ::ehtml($skype),
|
||||
image => 'skype.gif',
|
||||
title_ml => '.service.skype',
|
||||
image => $site->{imgfile},
|
||||
title_ml => $site->{title_ml},
|
||||
};
|
||||
push @ret, $service;
|
||||
};
|
||||
|
||||
my $services = DW::External::ProfileServices->list;
|
||||
my $accounts = $u->load_profile_accts;
|
||||
|
||||
if (%$accounts) {
|
||||
foreach my $site (@$services) {
|
||||
my $s_id = $site->{service_id};
|
||||
my $vals = $accounts->{$s_id} // [];
|
||||
|
||||
foreach my $acct (@$vals) {
|
||||
push @ret, $info->( $acct->[1], $site );
|
||||
}
|
||||
}
|
||||
|
||||
return \@ret;
|
||||
}
|
||||
|
||||
if ( my $tumblr = $u->prop('tumblr') ) {
|
||||
my $url = sprintf( "//%s.tumblr.com", LJ::eurl($tumblr) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'tumblr',
|
||||
text => LJ::ehtml($tumblr),
|
||||
url => $url,
|
||||
image => 'tumblr.png',
|
||||
title_ml => '.service.tumblr',
|
||||
};
|
||||
}
|
||||
# legacy path for users who still have data in userprops
|
||||
my $userprops = DW::External::ProfileServices->userprops;
|
||||
|
||||
if ( my $twitter = $u->prop('twitter') ) {
|
||||
my $url = sprintf( "//www.twitter.com/%s", LJ::eurl($twitter) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'twitter',
|
||||
text => LJ::ehtml($twitter),
|
||||
url => $url,
|
||||
image => 'twitter_bird.png',
|
||||
title_ml => '.service.twitter',
|
||||
};
|
||||
}
|
||||
$u->preload_props(@$userprops);
|
||||
|
||||
if ( my $wattpad = $u->prop('wattpad') ) {
|
||||
my $url = sprintf( "//www.wattpad.com/user/%s", LJ::eurl($wattpad) );
|
||||
push @ret,
|
||||
{
|
||||
type => 'wattpad',
|
||||
text => LJ::ehtml($wattpad),
|
||||
url => $url,
|
||||
image => 'wattpad.png',
|
||||
title_ml => '.service.wattpad',
|
||||
};
|
||||
foreach my $site (@$services) {
|
||||
if ( my $acct = $u->prop( $site->{userprop} ) ) {
|
||||
push @ret, $info->( $acct, $site );
|
||||
}
|
||||
}
|
||||
|
||||
return \@ret;
|
||||
|
|
|
@ -56,7 +56,7 @@ $LJ::DBIRole = new DBI::Role {
|
|||
"notifybookmarks", "embedcontent_preview", "logprop_history", "import_status",
|
||||
"externalaccount", "content_filters", "content_filter_data", "userpicmap3",
|
||||
"media", "collections", "collection_items", "logslugs",
|
||||
"media_versions", "media_props",
|
||||
"media_versions", "media_props", "user_profile_accts",
|
||||
);
|
||||
|
||||
# keep track of what db locks we have out
|
||||
|
@ -535,8 +535,10 @@ sub alloc_global_counter {
|
|||
# 'Z' == import status item, 'X' == eXternal account
|
||||
# 'F' == filter id, 'Y' = pic/keYword mapping id
|
||||
# 'A' == mediA item id, 'O' == cOllection id,
|
||||
# 'N' == collectioN item id
|
||||
# 'B' == api key id
|
||||
# 'N' == collectioN item id, 'B' == api key id,
|
||||
# 'P' == Profile account id
|
||||
#
|
||||
# remaining unused letters: G H J U W
|
||||
#
|
||||
sub alloc_user_counter {
|
||||
my ( $u, $dom, $opts ) = @_;
|
||||
|
@ -544,7 +546,7 @@ sub alloc_user_counter {
|
|||
|
||||
##################################################################
|
||||
# IF YOU UPDATE THIS MAKE SURE YOU ADD INITIALIZATION CODE BELOW #
|
||||
return undef unless $dom =~ /^[LTMPSRKCOVEQGDIZXFYAB]$/; #
|
||||
return undef unless $dom =~ /^[LTMPSRKCOVEQDIZXFYABN]$/;
|
||||
##################################################################
|
||||
|
||||
my $dbh = LJ::get_db_writer();
|
||||
|
@ -704,6 +706,11 @@ sub alloc_user_counter {
|
|||
$u->selectrow_array( "SELECT MAX(colitemid) FROM collection_items WHERE userid = ?",
|
||||
undef, $uid );
|
||||
}
|
||||
elsif ( $dom eq "P" ) {
|
||||
$newmax =
|
||||
$u->selectrow_array( "SELECT MAX(account_id) FROM user_profile_accts WHERE userid = ?",
|
||||
undef, $uid );
|
||||
}
|
||||
else {
|
||||
die "No user counter initializer defined for area '$dom'.\n";
|
||||
}
|
||||
|
|
|
@ -132,3 +132,5 @@ invite_code_try_ip:<ip> = stores a value of 1 for the IP address of the person w
|
|||
<uid> api_keys_list:<uid> = arrayref of all keyhashes owned by a given userid
|
||||
|
||||
profile_editors = arrayref of uids used by profile_save hook
|
||||
profile_services = arrayref of sorted hashrefs returned from profile_services table
|
||||
<uid> profile_accts:<uid> = hashref of user data from user_profile_accts table
|
||||
|
|
|
@ -20,6 +20,7 @@ body<=
|
|||
use strict;
|
||||
use vars qw(%POST %GET $headextra @errors);
|
||||
use LJ::Setting;
|
||||
use DW::External::ProfileServices;
|
||||
|
||||
LJ::need_res( { priority => $LJ::OLD_RES_PRIORITY }, 'stc/lj_settings.css' );
|
||||
|
||||
|
@ -47,17 +48,12 @@ body<=
|
|||
push @settings, "LJ::Setting::FindByEmail" if LJ::is_enabled('opt_findbyemail');
|
||||
push @settings, "DW::Setting::ProfileEmail";
|
||||
|
||||
my $dbr = LJ::get_db_reader();
|
||||
my $sth;
|
||||
my $profile_accts = $u->load_profile_accts( force_db => 1 );
|
||||
|
||||
# list the userprops that are handled explicitly by code on this page
|
||||
# props in this list will be preloaded on page load and saved on post
|
||||
my @uprops = qw/
|
||||
opt_whatemailshow comm_theme
|
||||
ao3 deviantart diigo discord etsy ffnet github google_talk
|
||||
icq insanejournal instagram jabber last_fm_user livejournal
|
||||
medium patreon pillowfort pinboard pinterest plurk ravelry
|
||||
reddit skype tumblr twitter wattpad
|
||||
url urlname gender
|
||||
opt_hidefriendofs opt_hidememberofs
|
||||
sidx_bdate sidx_bday
|
||||
|
@ -66,6 +62,12 @@ body<=
|
|||
opt_sharebday
|
||||
/;
|
||||
|
||||
my %legacy_service_props = map { $_ => 1 } @{ DW::External::ProfileServices->userprops };
|
||||
|
||||
unless (%$profile_accts) {
|
||||
push @uprops, $_ foreach keys %legacy_service_props;
|
||||
}
|
||||
|
||||
# load user props
|
||||
$u->preload_props( { use_master => 1 }, @uprops );
|
||||
|
||||
|
@ -403,45 +405,65 @@ body<=
|
|||
|
||||
$ret .= "<tr><td colspan=3><table summary=''>";
|
||||
my $oddeven = 0;
|
||||
my $ct = 1;
|
||||
|
||||
foreach my $p (
|
||||
["ao3", $ML{ '.services.ao3' }, 40],
|
||||
["deviantart", $ML{'.services.deviantart'}, 20],
|
||||
["diigo", $ML{ '.services.diigo' }, 16],
|
||||
["discord", $ML{ '.services.discord' }, 40],
|
||||
["etsy", $ML{ '.services.etsy' }, 20],
|
||||
["ffnet", $ML{ '.services.ffnet' }, 30],
|
||||
["github", $ML{'.services.github'}, 39],
|
||||
["google_talk", $ML{'.chat.googlehangouts'}, 60],
|
||||
["icq", $ML{'.chat.icquin'}, 12],
|
||||
["insanejournal", $ML{'.services.insanejournal'}, 15],
|
||||
["instagram", $ML{'.services.instagram'}, 30],
|
||||
["jabber", $ML{'.chat.jabber'}, 60],
|
||||
["last_fm_user", $ML{'.services.last_fm'}, 255],
|
||||
["livejournal", $ML{'.services.livejournal'}, 30],
|
||||
["medium", $ML{'.services.medium'}, 25],
|
||||
["patreon", $ML{'.services.patreon'}, 255],
|
||||
["pillowfort", $ML{'.services.pillowfort'}, 255],
|
||||
["pinboard", $ML{'.services.pinboard'}, 30],
|
||||
["pinterest", $ML{'.services.pinterest'}, 30],
|
||||
["plurk", $ML{ '.services.plurk' }, 255],
|
||||
["ravelry", $ML{ '.services.ravelry' }, 40],
|
||||
["reddit", $ML{ '.services.reddit' }, 20],
|
||||
["skype", $ML{'.chat.skype'}, 40],
|
||||
["tumblr", $ML{ '.services.tumblr' }, 255],
|
||||
["twitter", $ML{ '.services.twitter' }, 40],
|
||||
["wattpad", $ML{ '.services.wattpad' }, 20],
|
||||
)
|
||||
my $service_info = sub {
|
||||
my ($site) = @_;
|
||||
$site->{title} = LJ::Lang::ml( $site->{title_ml} );
|
||||
return $site;
|
||||
};
|
||||
|
||||
my @services = map $service_info->($_), @{ DW::External::ProfileServices->list };
|
||||
my @dropdown = ( '' => '' );
|
||||
push @dropdown, ( $_->{service_id} => $_->{title} ) foreach @services;
|
||||
|
||||
my $service_td = sub {
|
||||
my ( $p, $val ) = @_;
|
||||
$oddeven = !$oddeven;
|
||||
$ret .= "<tr class='field_block'>" if $oddeven;
|
||||
$ret .= "<td class='field_name' width='20%'>$p->{title}</td><td width='30%'>";
|
||||
$ret .= LJ::html_hidden( "extservice_site_$ct", $p->{service_id} );
|
||||
$ret .= LJ::html_hidden( "extservice_dbid_$ct", $val->[0] ) if $val->[0];
|
||||
$ret .= LJ::html_text( { name => "extservice_val_" . $ct++,
|
||||
value => $val->[1],
|
||||
title => $p->{title} . " Username",
|
||||
size => '20',
|
||||
maxlength => $p->{maxlen} } );
|
||||
$ret .= "</td>\n";
|
||||
$ret .= "</tr>" unless $oddeven;
|
||||
};
|
||||
|
||||
# ignore legacy userprops if %$profile_accts has been populated
|
||||
if (%$profile_accts) {
|
||||
foreach my $p ( @services ) {
|
||||
my $s_id = $p->{service_id};
|
||||
next unless $profile_accts->{$s_id};
|
||||
foreach my $val ( @{ $profile_accts->{$s_id} } ) {
|
||||
$service_td->( $p, $val ) ;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach my $p ( @services )
|
||||
{
|
||||
next unless $p->{userprop};
|
||||
my $val = $u->{$p->{userprop}};
|
||||
next unless $val;
|
||||
$service_td->( $p, [0, $val] ) ;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $n ( $ct .. 26 ) # same number of fields as static form
|
||||
{
|
||||
$oddeven = !$oddeven;
|
||||
$ret .= "<tr class='field_block'>" if $oddeven;
|
||||
$ret .= "<td class='field_name' width='20%'>$p->[1]</td><td width='30%'>";
|
||||
$ret .= LJ::html_text( { name => $p->[0],
|
||||
value => $u->{$p->[0]},
|
||||
title => $p->[1],
|
||||
$ret .= "<td class='field_name' width='20%'>";
|
||||
$ret .= LJ::html_select( { name => "extservice_site_$n" }, @dropdown );
|
||||
$ret .= "</td><td width='30%'>";
|
||||
$ret .= LJ::html_text( { name => "extservice_val_$n",
|
||||
value => '',
|
||||
title => "Username \#$n",
|
||||
size => '20',
|
||||
maxlength => $p->[2] } );
|
||||
maxlength => 255 } );
|
||||
$ret .= "</td>\n";
|
||||
$ret .= "</tr>" unless $oddeven;
|
||||
}
|
||||
|
@ -693,12 +715,32 @@ body<=
|
|||
# set userprops
|
||||
my %prop;
|
||||
foreach my $uprop (@uprops) {
|
||||
next if $legacy_service_props{$uprop};
|
||||
my $eff_val = $POST{$uprop}; # effective value, since 0 isn't stored
|
||||
$eff_val = "" unless $eff_val;
|
||||
$prop{$uprop} = $eff_val;
|
||||
}
|
||||
$u->set_prop( \%prop, undef, { skip_db => 1 } );
|
||||
|
||||
# update external services
|
||||
my %services = map { $_->{service_id} => $_ } @{ DW::External::ProfileServices->list };
|
||||
my %new_accts;
|
||||
|
||||
foreach my $ct (1..26) {
|
||||
my $s_id = $POST{"extservice_site_$ct"};
|
||||
next unless $s_id;
|
||||
my $val = $POST{"extservice_val_$ct"} // '';
|
||||
$val = LJ::text_trim( $val, 255, $services{$s_id}->{maxlen} );
|
||||
$new_accts{$s_id} //= [];
|
||||
if ( my $a_id = $POST{"extservice_dbid_$ct"} ) {
|
||||
push @{ $new_accts{$s_id} }, [ $a_id, $val ];
|
||||
} else {
|
||||
push @{ $new_accts{$s_id} }, $val;
|
||||
}
|
||||
}
|
||||
|
||||
$u->save_profile_accts( \%new_accts );
|
||||
|
||||
# location or bday could've changed... (who cares about checking exactly)
|
||||
$u->invalidate_directory_record;
|
||||
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
;; -*- coding: utf-8 -*-
|
||||
.chat.googlehangouts=Google Chat
|
||||
|
||||
.chat.icquin=ICQ UIN
|
||||
|
||||
.chat.jabber=Jabber
|
||||
|
||||
.chat.skype=Skype
|
||||
|
||||
.comms2=Display communities of which you are a member or administrator.
|
||||
|
||||
.display.title=Display Settings for [[name]]
|
||||
|
@ -179,50 +171,6 @@
|
|||
|
||||
.select.provider=-- Select Carrier --
|
||||
|
||||
.services.ao3=AO3 Username
|
||||
|
||||
.services.deviantart=DeviantArt Username
|
||||
|
||||
.services.diigo=Diigo Username
|
||||
|
||||
.services.discord=Discord Username
|
||||
|
||||
.services.etsy=Etsy Username
|
||||
|
||||
.services.ffnet=FanFiction.net Username
|
||||
|
||||
.services.github=GitHub Username
|
||||
|
||||
.services.insanejournal=InsaneJournal Username
|
||||
|
||||
.services.instagram=Instagram Username
|
||||
|
||||
.services.last_fm=Last.fm Username
|
||||
|
||||
.services.livejournal=LiveJournal Username
|
||||
|
||||
.services.medium=Medium Username
|
||||
|
||||
.services.patreon=Patreon Username
|
||||
|
||||
.services.pillowfort=Pillowfort Username
|
||||
|
||||
.services.pinboard=Pinboard Username
|
||||
|
||||
.services.pinterest=Pinterest Username
|
||||
|
||||
.services.plurk=Plurk Username
|
||||
|
||||
.services.ravelry=Ravelry Username
|
||||
|
||||
.services.reddit=Reddit Username
|
||||
|
||||
.services.tumblr=Tumblr Username
|
||||
|
||||
.services.twitter=Twitter Username
|
||||
|
||||
.services.wattpad=Wattpad Username
|
||||
|
||||
.show.birthday.day2=Show only month and day
|
||||
|
||||
.show.birthday.full2=Show month, day, and year
|
||||
|
|
|
@ -101,58 +101,6 @@
|
|||
|
||||
.section.edit=Edit
|
||||
|
||||
.service.ao3=Archive of Our Own
|
||||
|
||||
.service.deviantart=DeviantArt
|
||||
|
||||
.service.diigo=Diigo
|
||||
|
||||
.service.discord=Discord
|
||||
|
||||
.service.etsy=Etsy
|
||||
|
||||
.service.ffnet=FanFiction.net
|
||||
|
||||
.service.github=GitHub
|
||||
|
||||
.service.hangouts=Google Chat
|
||||
|
||||
.service.icq=ICQ
|
||||
|
||||
.service.insanejournal=InsaneJournal
|
||||
|
||||
.service.instagram=Instagram
|
||||
|
||||
.service.jabber=Jabber
|
||||
|
||||
.service.lastfm=Last.fm
|
||||
|
||||
.service.livejournal=LiveJournal
|
||||
|
||||
.service.medium=Medium
|
||||
|
||||
.service.patreon=Patreon
|
||||
|
||||
.service.pillowfort=Pillowfort
|
||||
|
||||
.service.pinboard=Pinboard
|
||||
|
||||
.service.pinterest=Pinterest
|
||||
|
||||
.service.plurk=Plurk
|
||||
|
||||
.service.ravelry=Ravelry
|
||||
|
||||
.service.reddit=Reddit
|
||||
|
||||
.service.skype=Skype
|
||||
|
||||
.service.tumblr=Tumblr
|
||||
|
||||
.service.twitter=Twitter
|
||||
|
||||
.service.wattpad=Wattpad
|
||||
|
||||
.title=Profile
|
||||
|
||||
.title.communityprofile=Community Profile
|
||||
|
|
Loading…
Reference in New Issue