[Slashdotjp-dev 931] [449] merged from upstream T_2_5_0_190

Back to archive index

svnno****@sourc***** svnno****@sourc*****
2008年 1月 22日 (火) 19:59:05 JST


Revision: 449
          http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi?root=slashdotjp&view=rev&rev=449
Author:   tach
Date:     2008-01-22 19:59:02 +0900 (Tue, 22 Jan 2008)

Log Message:
-----------
merged from upstream T_2_5_0_190

Modified Paths:
--------------
    slashjp/branches/upstream/current/Slash/DB/MySQL/MySQL.pm
    slashjp/branches/upstream/current/Slash/Slash.pm
    slashjp/branches/upstream/current/Slash/Utility/Data/Data.pm
    slashjp/branches/upstream/current/Slash/Utility/Environment/Environment.pm
    slashjp/branches/upstream/current/plugins/Ajax/htdocs/ajax.pl
    slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/common.js
    slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/sd_autocomplete.js
    slashjp/branches/upstream/current/plugins/FireHose/FireHose.pm
    slashjp/branches/upstream/current/plugins/FireHose/firehose.pl
    slashjp/branches/upstream/current/plugins/FireHose/firehose_get_thumbnails.pl
    slashjp/branches/upstream/current/plugins/FireHose/firehose_reject_old.pl
    slashjp/branches/upstream/current/plugins/FireHose/templates/firehose_tabs;misc;default
    slashjp/branches/upstream/current/plugins/FireHose/templates/list;firehose;default
    slashjp/branches/upstream/current/plugins/FireHose/templates/view;firehose;default
    slashjp/branches/upstream/current/plugins/HumanConf/Static/Static.pm
    slashjp/branches/upstream/current/plugins/Stats/Stats.pm
    slashjp/branches/upstream/current/plugins/Stats/adminmail.pl
    slashjp/branches/upstream/current/plugins/Stats/templates/display;adminmail;default
    slashjp/branches/upstream/current/plugins/Tags/Tags.pm
    slashjp/branches/upstream/current/plugins/Tags/tags_udc.pl
    slashjp/branches/upstream/current/sbin/portald
    slashjp/branches/upstream/current/sql/mysql/defaults.sql
    slashjp/branches/upstream/current/sql/mysql/upgrades
    slashjp/branches/upstream/current/tagboxes/Despam/Despam.pm
    slashjp/branches/upstream/current/tagboxes/FireHoseScores/FireHoseScores.pm
    slashjp/branches/upstream/current/themes/slashcode/htdocs/images/comments.js
    slashjp/branches/upstream/current/themes/slashcode/tasks/balance_readers.pl
    slashjp/branches/upstream/current/themes/slashcode/tasks/ircslash.pl
    slashjp/branches/upstream/current/themes/slashcode/templates/printCommComments;misc;default
    slashjp/branches/upstream/current/themes/slashcode/templates/printCommentsMain;misc;default

Added Paths:
-----------
    slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/
    slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/Makefile.PL
    slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/PasswordSalt.pm


-------------- next part --------------
Added: slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/Makefile.PL
===================================================================
--- slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/Makefile.PL	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/Makefile.PL	2008-01-22 10:59:02 UTC (rev 449)
@@ -0,0 +1,6 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+	'NAME'	=> 'Slash::Apache::User::PasswordSalt',
+	'VERSION_FROM' => 'PasswordSalt.pm',
+);

Added: slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/PasswordSalt.pm
===================================================================
--- slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/PasswordSalt.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/Slash/Apache/User/PasswordSalt/PasswordSalt.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -0,0 +1,96 @@
+# This code is a part of Slash, and is released under the GPL.
+# Copyright 1997-2005 by Open Source Technology Group. See README
+# and COPYING for more information, or see http://slashcode.com/.
+# $Id: PasswordSalt.pm,v 1.1 2008/01/14 23:46:52 jamiemccarthy Exp $
+
+package Slash::Apache::User::PasswordSalt;
+
+use strict;
+use File::Spec::Functions;
+use Slash::Utility::Environment;
+use vars qw($REVISION $VERSION);
+
+$VERSION   	= '2.003000';  # v2.3.0
+($REVISION)	= ' $Revision: 1.1 $ ' =~ /\$Revision:\s+([^\s]+)/;
+
+
+#
+# To make your Slash installation more secure, create a file at
+# /usr/local/slash/slash.salts which contains password salt for
+# each of your Slash site virtual users.  A site only needs one
+# salt, so your initial setup should contain one scalar value.
+# Later, if there is a security issue such as a vulnerability
+# that allows user password MD5's to be read from your database,
+# or a no-longer-trusted employee being dismissed, you should
+# append another scalar onto the list.
+#
+# For example, if you are running one Slash site with the virtual
+# user 'foo', you might create a file with the contents:
+#	foo	kL3xeJm6az
+# If your site then suffers from a vulnerability, then after the
+# vulnerability is fixed, you might add another scalar:
+#	foo	kL3xeJm6az	2dJ4oSI4e9
+# The format is simply whitespace-separated columns, first the
+# virtual user and then the salts.
+#
+# This salt combines with each user's password before the one-way
+# MD5 function stores them in the database.  An attacker who only
+# has access to the database, not the salt file, and obtains one
+# or more hashed passwords from there will find reversing the
+# hashes to determine the original passwords made significantly
+# more difficult, depending on how many random bits are in the salt.
+#
+# If the attacker gains access to both the database and to the file,
+# they will easily be able to brute-force decipher users' passwords.
+# (They will not be able to use precomputed hash dictionaries, but
+# this is only a minor setback.)
+#
+
+
+my $salt_hr = undef;
+
+
+sub loadSalts {
+	return if defined $salt_hr;
+
+	$salt_hr = { };
+	my $constants = getCurrentStatic();
+	my $slashdir = $constants->{slashdir} || '/usr/local/slash';
+	my $saltsfile = 'slash.salts';
+	my $fullfile = catfile($slashdir, $saltsfile);
+	return if !-e $fullfile || !-r _ || !-s _;
+	return if !open(my $fh, $fullfile);
+	while (<$fh>) {
+		chomp;
+		s/\s*$//;
+		my($vu, @salts) = split;
+		$salt_hr->{$vu} = [ @salts ] if $vu;
+	}
+	close $fh;
+}
+
+sub getSalts {
+	my($virtual_user) = @_;
+	loadSalts();
+	return $salt_hr->{$virtual_user} || [ ];
+}
+
+sub getCurrentSalt {
+	my($virtual_user) = @_;
+	my $salt_ar = getSalts($virtual_user);
+	return @$salt_ar ? $salt_ar->[-1] : '';
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Slash::Apache::User::PasswordSalt - Salt user passwords for security
+
+=head1 SEE ALSO
+
+Slash::Apache::User(3).
+
+=cut

Modified: slashjp/branches/upstream/current/Slash/DB/MySQL/MySQL.pm
===================================================================
--- slashjp/branches/upstream/current/Slash/DB/MySQL/MySQL.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/Slash/DB/MySQL/MySQL.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: MySQL.pm,v 1.998 2007/12/12 22:03:20 jamiemccarthy Exp $
+# $Id: MySQL.pm,v 1.1000 2008/01/15 00:17:38 jamiemccarthy Exp $
 
 package Slash::DB::MySQL;
 use strict;
@@ -20,7 +20,7 @@
 use base 'Slash::DB::Utility';
 use Slash::Constants ':messages';
 
-($VERSION) = ' $Revision: 1.998 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.1000 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 # Fry: How can I live my life if I can't tell good from evil?
 
@@ -1583,8 +1583,7 @@
 	}
 
 	if ($kind != $PUBLIC && $kind != $LOGTOKEN && !$uid_verified) {
-		my $cryptpasswd = encryptPassword($passwd);
-		my @pass = $self->sqlSelect(
+		my($db_uid, $db_passwd, $db_newpasswd) = $self->sqlSelect(
 			'uid,passwd,newpasswd',
 			'users',
 			"uid=$uid_try_q"
@@ -1592,8 +1591,8 @@
 
 		# try ENCRYPTED -> ENCRYPTED
 		if ($kind == $EITHER || $kind == $ENCRYPTED) {
-			if ($passwd eq $pass[$PASSWD]) {
-				$uid_verified = $pass[$UID];
+			if (comparePassword($passwd, $db_passwd, 0, ($kind == $ENCRYPTED))) {
+				$uid_verified = $db_uid;
 				# get existing logtoken, if exists, or new one
 				$cookpasswd = $self->getLogToken($uid_verified, 1);
 			}
@@ -1601,8 +1600,8 @@
 
 		# try PLAINTEXT -> ENCRYPTED
 		if (($kind == $EITHER || $kind == $PLAIN) && !$uid_verified) {
-			if ($cryptpasswd eq $pass[$PASSWD]) {
-				$uid_verified = $pass[$UID];
+			if (comparePassword($passwd, $db_passwd, ($kind == $PLAIN), 0)) {
+				$uid_verified = $db_uid;
 				# get existing logtoken, if exists, or new one
 				$cookpasswd = $self->getLogToken($uid_verified, 1);
 			}
@@ -1610,14 +1609,15 @@
 
 		# try PLAINTEXT -> NEWPASS
 		if (($kind == $EITHER || $kind == $PLAIN) && !$uid_verified) {
-			if ($passwd eq $pass[$NEWPASSWD]) {
+			if ($passwd eq $db_newpasswd) {
+				my $cryptpasswd = encryptPassword($passwd);
 				$self->sqlUpdate('users', {
 					newpasswd	=> '',
 					passwd		=> $cryptpasswd
 				}, "uid=$uid_try_q");
 				$newpass = 1;
 
-				$uid_verified = $pass[$UID];
+				$uid_verified = $db_uid;
 				# delete existing logtokens
 				$self->deleteLogToken($uid_verified, 1);
 				# create new logtoken
@@ -9970,53 +9970,43 @@
 	return $self->sqlSelectAllHashrefArray("*", "topic_param", "tid = $tid_q");
 }
 
-########################################################
-# As of 2004/04:
+# getStoryTopics returns a hashref whose keys are the tids
+# chosen for a story.  The values depend on $add_names:
+#
 # $add_names of 1 means to return the alt text, which is the
 # human-readable name of a topic.  This is currently used
 # only in article.pl to add these words and phrases to META
 # information on the webpage.
+#
 # $add_names of 2 means to return the name, which is a
 # (not-guaranteed-unique) short single keyword.  This is
 # currently used only in adminmail.pl to append something
 # descriptive to the numeric tid for the topichits_123_foo
 # stats.
+#
+# Any other $add_names value means to return '1' for values.
+
 sub getStoryTopics {
 	my($self, $id, $add_names) = @_;
-	my($topicdesc);
 
 	my $stoid = $self->getStoidFromSidOrStoid($id);
 	return undef unless $stoid;
 
-	my $topics = $self->sqlSelectAll(
+	my $topics = $self->sqlSelectColArrayref(
 		'tid',
 		'story_topics_chosen',
 		"stoid=$stoid"
 	);
 
-	# All this to avoid a join. :/
-	#
-	# Poor man's hash assignment from an array for the short names.
-	# XXX This really should be done by pulling data from
-	# getTopicTree()
-	$topicdesc =  {
-		map { @{$_} }
-		@{$self->sqlSelectAll(
-			'tid, keyword',
-			'topics'
-		)}
-	} if $add_names == 2;
+	my $field = '';
+	if ($add_names == 1) {		$field = 'textname'	}
+	elsif ($add_names == 2) {	$field = 'keyword'	}
 
-	# We use a Description for the long names.
-	$topicdesc = $self->getDescriptions('topics')
-		if !$topicdesc && $add_names;
-
-	my $answer;
-	for my $topic (@$topics) {
-		$answer->{$topic->[0]} = $add_names && $topicdesc
-			? $topicdesc->{$topic->[0]} : 1;
+	my $tree = $self->getTopicTree();
+	my $answer = { };
+	for my $tid (keys %$tree) {
+		$answer->{$tid} = $field ? $tree->{$tid}{$field} : 1;
 	}
-
 	return $answer;
 }
 

Modified: slashjp/branches/upstream/current/Slash/Slash.pm
===================================================================
--- slashjp/branches/upstream/current/Slash/Slash.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/Slash/Slash.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Slash.pm,v 1.342 2007/12/13 20:54:55 pudge Exp $
+# $Id: Slash.pm,v 1.343 2008/01/18 22:36:50 pudge Exp $
 
 package Slash;
 
@@ -77,6 +77,17 @@
 
 	my $discussion2 = discussion2($user);
 
+	# it's a bit of a drag, but ... oh well! 
+	# print_cchp gets messed up with d2, so we just punt and have
+	# selectComments called twice if necessary, the first time doing
+	# print_cchp, then blanking that out so it is not done again -- pudge
+	if ($discussion2 && $form->{ssi} && $form->{ssi} eq 'yes' && $form->{cchp}) {
+		$user->{discussion2} = 'none';
+		selectComments($discussion, $cid, $options);
+		$user->{discussion2} = $discussion2;
+		delete $form->{cchp};
+	}
+
 	my $commentsort = defined $options->{commentsort}
 		? $options->{commentsort}
 		: $user->{commentsort};
@@ -323,11 +334,12 @@
 	# also consolidate code with ajax.pl:fetchComments
 	# version 0.9 is broken; 0.6 and 1.00 seem to work -- pudge 2006-12-19
 	require Data::JavaScript::Anon;
-	my($slashdb, $constants, $user, $form) = @_;
+	my($slashdb, $constants, $user, $form, $gSkin) = @_;
 	$slashdb   ||= getCurrentDB();
 	$constants ||= getCurrentStatic();
 	$user      ||= getCurrentUser();
 	$form      ||= getCurrentForm();
+	$gSkin     ||= getCurrentSkin();
 
 	my $id = $form->{sid};
 	my $pid = $form->{cid} || 0;
@@ -404,8 +416,14 @@
 
 	# maybe also check if this ad should be running with some other var?
 	# from ads table? -- pudge
-	if ($constants->{run_ads} && $constants->{run_ads_inline_comments}) {
-		$extra .= "adTimerUrl = '$constants->{run_ads_inline_comments}';\n";
+	if ( $constants->{run_ads}
+	 && !$user->{state}{page_adless}
+	 && !$user->{state}{page_buying}
+	 &&  $user->{currentSkin} ne 'admin'
+	 &&  $constants->{run_ads_inline_comments}
+	) {
+		(my $url = $constants->{run_ads_inline_comments}) =~ s/<topic>/$gSkin->{name}/g;
+		$extra .= "adTimerUrl = '$url';\n";
 	}
 
 	return <<EOT;

Modified: slashjp/branches/upstream/current/Slash/Utility/Data/Data.pm
===================================================================
--- slashjp/branches/upstream/current/Slash/Utility/Data/Data.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/Slash/Utility/Data/Data.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Data.pm,v 1.208 2007/10/10 20:45:07 jamiemccarthy Exp $
+# $Id: Data.pm,v 1.210 2008/01/18 21:28:41 jamiemccarthy Exp $
 
 package Slash::Utility::Data;
 
@@ -43,6 +43,7 @@
 use Safe;
 use Slash::Constants qw(:strip);
 use Slash::Utility::Environment;
+use Slash::Apache::User::PasswordSalt;
 use URI;
 use XML::Parser;
 
@@ -61,7 +62,7 @@
 	$HTML::Tagset::linkElements{slash} = ['src', 'href'];
 }
 
-($VERSION) = ' $Revision: 1.208 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.210 $ ' =~ /\$Revision:\s+([^\s]+)/;
 @EXPORT	   = qw(
 	addDomainTags
 	createStoryTopicData
@@ -75,6 +76,7 @@
 	cleanRedirectUrl
 	cleanRedirectUrlFromForm
 	commify
+	comparePassword
 	countTotalVisibleKids
 	countWords
 	createSid
@@ -203,6 +205,7 @@
 
 sub emailValid {
 	my($email) = @_;
+	return 0 if !$email;
 
 	my $constants = getCurrentStatic();
 	return 0 if $constants->{email_domains_invalid}
@@ -772,8 +775,10 @@
 
 =head2 encryptPassword(PASSWD)
 
-Encrypts given password.  Currently uses MD5, but could change in the future,
-so do not depend on implementation.
+Encrypts given password, using the most recent salt (if any) in
+Slash::Apache::User::PasswordSalt for the current virtual user.
+Currently uses MD5, but could change in the future, so do not
+depend on the implementation.
 
 =over 4
 
@@ -797,11 +802,80 @@
 
 sub encryptPassword {
 	my($passwd) = @_;
-	return md5_hex($passwd);
+	my $slashdb = getCurrentDB();
+	my $vu = $slashdb->{virtual_user};
+	my $salt = Slash::Apache::User::PasswordSalt::getCurrentSalt($vu);
+	return md5_hex("$salt$passwd");
 }
 
 #========================================================================
 
+=head2 comparePassword(PASSWD, MD5, ISPLAIN, ISENC)
+
+Given a password and an MD5 hex string, compares the two to see if they
+represent the same value.  To be precise:
+
+If the password given is equal to the MD5 string, it must already be
+in MD5 format and be correct, so return true
+
+Otherwise, the password is assumed to be plaintext.  Each possible
+salt-encryption of it (including the encryption with empty salt) is
+compared against the MD5 string.  True is returned if there is any
+match.
+
+If ISPLAIN is true, PASSWD is assumed to be plaintext, so the
+(trivial equality) test against the encrypted MD5 is not performed.
+
+If ISENC is true, PASSWD is assumed to be already encrypted, so the
+tests of salting and encrypting it are not performed.
+
+(If neither is true, all tests are performed.  If both are true, no
+tests are performed and 0 is returned.)
+
+=over 4
+
+=item Parameters
+
+=over 4
+
+=item PASSWD
+
+Possibly-correct password, either plaintext or already-MD5's,
+to be checked.
+
+=item MD5
+
+Encrypted correct password.
+
+=back
+
+=item Return value
+
+0 or 1.
+
+=back
+
+=cut
+
+sub comparePassword {
+	my($passwd, $md5, $is_plain, $is_enc) = @_;
+	if (!$is_plain) {
+		return 1 if $passwd eq $md5;
+	}
+	if (!$is_enc) {
+		return 1 if md5_hex($passwd) eq $md5;
+		my $slashdb = getCurrentDB();
+		my $vu = $slashdb->{virtual_user};
+		my $salt_ar = Slash::Apache::User::PasswordSalt::getSalts($vu);
+		for my $salt (reverse @$salt_ar) {
+			return 1 if md5_hex("$salt$passwd") eq $md5;
+		}
+	}
+	return 0;
+}
+
+#========================================================================
+
 =head2 stripByMode(STRING [, MODE, NO_WHITESPACE_FIX])
 
 Private function.  Fixes up a string based on what the mode is.  This
@@ -4367,4 +4441,4 @@
 
 =head1 VERSION
 
-$Id: Data.pm,v 1.208 2007/10/10 20:45:07 jamiemccarthy Exp $
+$Id: Data.pm,v 1.210 2008/01/18 21:28:41 jamiemccarthy Exp $

Modified: slashjp/branches/upstream/current/Slash/Utility/Environment/Environment.pm
===================================================================
--- slashjp/branches/upstream/current/Slash/Utility/Environment/Environment.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/Slash/Utility/Environment/Environment.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Environment.pm,v 1.226 2008/01/09 19:57:54 jamiemccarthy Exp $
+# $Id: Environment.pm,v 1.227 2008/01/18 22:36:50 pudge Exp $
 
 package Slash::Utility::Environment;
 
@@ -33,7 +33,7 @@
 use base 'Exporter';
 use vars qw($VERSION @EXPORT);
 
-($VERSION) = ' $Revision: 1.226 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.227 $ ' =~ /\$Revision:\s+([^\s]+)/;
 @EXPORT	   = qw(
 
 	dbAvailable
@@ -1573,16 +1573,10 @@
 	$user->{state}{no_d2} = $form->{no_d2} ? 1 : 0;
 	$user->{discussion2} ||= 'none';
 
-	# pct of anon users get this
+	# most anon users get this
 	if ($user->{is_anon} && !$user->{state}{no_d2}) {
-#		my $i = hex(substr($user->{srcids}{16}, -2));
-		$hostip =~ /^(\d+).(\d+).(\d+).(\d+)$/;
-		my $i = $2;
-
-#		# for (0..255) { $x = ((($_-1)/256) < .1); last if !$x; printf "%d:%d\n", $_, $x; }
-		if ($ENV{GATEWAY_INTERFACE} && ( $i == 144 || ((($i-1)/256) < .5) ) ) {  # 10 percent, x.(0..3).y.z
-			my $d2 = 'slashdot';
-
+		my $d2 = 'slashdot';
+		if ($ENV{GATEWAY_INTERFACE}) {
 			# get user-agent (ENV not populated yet)
 			my %headers = $r->headers_in;
 			# just in case:
@@ -1591,9 +1585,8 @@
 			if ($ua =~ /MSIE (\d+)/) {
 				$d2 = 'none';# if $1 < 7;
 			}
-
-			$user->{discussion2} = $d2;
 		}
+		$user->{discussion2} = $d2;
 	}
 
 	# All sorts of checks on user data.  The story_{never,always} checks
@@ -3506,4 +3499,4 @@
 
 =head1 VERSION
 
-$Id: Environment.pm,v 1.226 2008/01/09 19:57:54 jamiemccarthy Exp $
+$Id: Environment.pm,v 1.227 2008/01/18 22:36:50 pudge Exp $

Modified: slashjp/branches/upstream/current/plugins/Ajax/htdocs/ajax.pl
===================================================================
--- slashjp/branches/upstream/current/plugins/Ajax/htdocs/ajax.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Ajax/htdocs/ajax.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: ajax.pl,v 1.65 2007/12/19 18:47:11 entweichen Exp $
+# $Id: ajax.pl,v 1.66 2008/01/18 22:36:50 pudge Exp $
 
 use strict;
 use warnings;
@@ -14,7 +14,7 @@
 use Slash::Utility;
 use vars qw($VERSION);
 
-($VERSION) = ' $Revision: 1.65 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.66 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 ##################################################################
 sub main {
@@ -361,12 +361,20 @@
 		}
 
 		if (@$special_cids) {
-			my @cid_data = map {{
-				uid    => $comments->{$_}{uid},
-				pid    => $comments->{$_}{pid},
-				points => $comments->{$_}{points},
-				kids   => []
-			}} @$special_cids;
+			my @cid_data;
+			for my $cid (@$special_cids) {
+				my $comments_new = {
+					uid     => $comments->{$cid}{uid},
+					pid     => $comments->{$cid}{pid},
+					points  => $comments->{$cid}{points},
+					kids    => []
+				};
+				if ($comments->{$cid}{subject_orig} && $comments->{$cid}{subject_orig} eq 'no') {
+					$comments_new->{subject} = $comments->{$cid}{subject};
+					$comments->{$cid}{subject} = 'Re:';
+				}
+				push @cid_data, $comments_new;
+			}
 
 			$data{new_cids_order} = [ @$special_cids ];
 			$data{new_cids_data}  = \@cid_data;

Modified: slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/common.js
===================================================================
--- slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/common.js	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/common.js	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,5 +1,5 @@
 // _*_ Mode: JavaScript; tab-width: 8; indent-tabs-mode: true _*_
-// $Id: common.js,v 1.159 2008/01/09 21:01:25 pudge Exp $
+// $Id: common.js,v 1.160 2008/01/21 18:42:37 tvroom Exp $
 
 // global settings, but a firehose might use a local settings object instead
 var firehose_settings = {};
@@ -16,6 +16,7 @@
   firehose_settings.removals = null;
   firehose_settings.is_embedded = 0;
   firehose_settings.not_id = 0;
+  firehose_settings.section = 0;
 
 // globals we haven't yet decided to move into |firehose_settings|
 var fh_play = 0;
@@ -621,6 +622,17 @@
 			firehose_get_updates({ oneupdate: 1 });
 		}
 	};
+
+	if (name == 'tabsection') {
+		firehose_settings.section = value;
+		params['tabtype'] = 'tabsection';
+	}
+
+	if (name == 'tabtype') {
+		params['tabtype'] = value;
+	}
+
+	params['section'] = firehose_settings.section;
 	ajax_update(params, '', handlers);
 }
 
@@ -1090,6 +1102,7 @@
 	params['startdate'] = firehose_settings.startdate;
 	params['duration'] = firehose_settings.duration;
 	params['issue'] = firehose_settings.issue;
+	params['section'] = firehose_settings.section;
 	params['page'] = page;
 	params['not_id'] = firehose_settings.not_id;
 	if ( firehose_settings.is_embedded ) {

Modified: slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/sd_autocomplete.js
===================================================================
--- slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/sd_autocomplete.js	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Ajax/htdocs/images/sd_autocomplete.js	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,5 +1,5 @@
 // _*_ Mode: JavaScript; tab-width: 8; indent-tabs-mode: true _*_
-// $Id: sd_autocomplete.js,v 1.42 2007/12/18 19:52:37 scc Exp $
+// $Id: sd_autocomplete.js,v 1.44 2008/01/18 22:36:50 pudge Exp $
 
 YAHOO.namespace("slashdot");
 
@@ -508,7 +508,11 @@
           me._hide();
           break;
         case 13:
-          me._completer.unmatchedItemSelectEvent.fire(me._completer, me, me._completer._sCurQuery);
+        	// I'm sorry to say we have to test first, something somehow somewhere can still leave
+        	//	leave this listener dangling; want to look deeper into this, as this would _still_
+        	//	leave the listener dangling
+        	if ( me._completer )
+          	me._completer.unmatchedItemSelectEvent.fire(me._completer, me, me._completer._sCurQuery);
           break;
         default:
           if ( me._pending_hide )

Modified: slashjp/branches/upstream/current/plugins/FireHose/FireHose.pm
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/FireHose.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/FireHose.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: FireHose.pm,v 1.197 2008/01/09 20:04:51 jamiemccarthy Exp $
+# $Id: FireHose.pm,v 1.199 2008/01/18 21:18:20 tvroom Exp $
 
 package Slash::FireHose;
 
@@ -41,7 +41,7 @@
 use base 'Slash::DB::MySQL';
 use vars qw($VERSION);
 
-($VERSION) = ' $Revision: 1.197 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.199 $ ' =~ /\$Revision:\s+([^\s]+)/;
 sub createFireHose {
 	my($self, $data) = @_;
 	$data->{dept} ||= "";
@@ -298,7 +298,6 @@
 		if ($id) {
 			# If a story is getting its primary skid to an ignored value set its firehose entry to non-public
 			my $public = ($story->{neverdisplay} || $ignore_skids{$story->{primaryskid}}) ? "no" : "yes";
-			print STDERR "Stoid: $story->{stoid} FHID: $id Public: $public ND: $story->{neverdisplay}\n";
 			my $data = {
 				title 		=> $story->{title},
 				uid		=> $story->{uid},
@@ -795,7 +794,9 @@
 	my $url = $url_id ? $self->getUrl($url_id) : undef;
 	$url = $url->{url} if $url;
 	my $url_prepend = $url ? qq{<a href="$url">$url</a>}   : '';
-	my $text = qq{$url_prepend $item->introtext $item->{bodytext}};
+	my $intro = $item->{introtext} || '';
+	my $body = $item->{bodytext} || '';
+	my $text = "$url_prepend $intro $body";
 
 	my %urls = ( );
 	my $tokens = HTML::TokeParser->new(\$text);
@@ -829,13 +830,15 @@
 		my $url_id_q = $self->sqlQuote($url_id);
 		my $count = $self->sqlCount("firehose", "url_id=$url_id_q");
 		if ($count > 0) {
-			my($uid, $id) = $self->sqlSelect("uid,id", "firehose", "url_id = $url_id_q", "order by id asc");
+			my($uid, $id) = $self->sqlSelect("uid,id",
+				"firehose", "url_id = $url_id_q", "ORDER BY id ASC");
 			if (isAnon($uid)) {
 				$ret_val = $id;
 			} else {
 				# Logged in, give precedence to most recent submission
 				my $uid_q = $self->sqlQuote($uid);
-				my($submitted_id) = $self->sqlSelect("id", "firehose", "url_id = $url_id_q AND uid=$uid_q", "order by id desc");
+				my($submitted_id) = $self->sqlSelect("id",
+					"firehose", "url_id = $url_id_q AND uid=$uid_q", "ORDER BY id DESC");
 				$ret_val = $submitted_id ? $submitted_id : $id;
 			}
 		}
@@ -988,7 +991,7 @@
 	my $firehose = getObject("Slash::FireHose");
 	my $opts = $firehose->getAndSetOptions();
 	my $html = {};
-	$html->{fhtablist} = slashDisplay("firehose_tabs", { nodiv => 1, tabs => $opts->{tabs}, options => $opts }, { Return => 1});
+	$html->{fhtablist} = slashDisplay("firehose_tabs", { nodiv => 1, tabs => $opts->{tabs}, options => $opts, section => $form->{section} }, { Return => 1});
 	$html->{fhoptions} = slashDisplay("firehose_options", { nowrapper => 1, options => $opts }, { Return => 1});
 	$html->{fhadvprefpane} = slashDisplay("fhadvprefpane", { options => $opts }, { Return => 1});
 
@@ -1271,6 +1274,7 @@
 	}, { Return => 1 });
 
 	$html->{local_last_update_time} = timeCalc($slashdb->getTime(), "%H:%M");
+	$html->{filter_text} = "Filtered to '".strip_literal($opts->{fhfilter})."'";
 	$html->{gmt_update_time} = " (".timeCalc($slashdb->getTime(), "%H:%M", 0)." GMT) " if $user->{is_admin};
 	$html->{itemsreturned} = $num_items == 0 ?  getData("noitems", { options => $opts }, 'firehose') : "";
 
@@ -1619,15 +1623,23 @@
 	my $user 	= getCurrentUser();
 	my $constants 	= getCurrentStatic();
 	my $form 	= getCurrentForm();
+	my $gSkin	= getCurrentSkin();
+
 	$opts 	        ||= {};
 	my $options 	= {};
 
 	my $types = { feed => 1, bookmark => 1, submission => 1, journal => 1, story => 1, vendor => 1, misc => 1 }; 
+	my $tabtypes = { tabsection => 1, tabpopular => 1, tabrecent => 1, tabuser => 1};
+	
+	my $tabtype = $tabtypes->{$form->{tabtype}} ? $form->{tabtype} : '';
+
+
 	my $modes = { full => 1, fulltitle => 1 };
 	my $pagesizes = { "small" => 1, "large" => 1 };
 
 	my $no_saved = $form->{no_saved};
 	$opts->{no_set} ||= $no_saved;
+	$opts->{initial} ||= 0;
 
 	if (defined $form->{mixedmode} && $form->{setfield}) {
 		$options->{mixedmode} = $form->{mixedmode} ? 1 : 0;
@@ -1651,9 +1663,11 @@
 	$options->{pause} = defined $user->{firehose_pause} ? $user->{firehose_pause} : 1;
 	$form->{pause} = 1 if $no_saved;
 
+	my $firehose_page = $user->{state}{firehose_page} || '';
+
 	if (defined $form->{pause}) {
 		$options->{pause} = $user->{firehose_paused} = $form->{pause} ? 1 : 0;
-		if (!$user->{state}{firehose_page} eq "user") {
+		if ($firehose_page ne 'user') {
 			$self->setUser($user->{uid}, { firehose_paused => $options->{pause} });
 		}
 	}
@@ -1721,7 +1735,42 @@
 
 	my $fhfilter;
 
+	if ($opts->{initial} && !$tabtype) {
+		$tabtype = 'tabsection';
+		$form->{section} = $gSkin->{skid} == $constants->{mainpage_skid} ? 0 : $gSkin->{skid};
+	}
 
+	my $the_skin = $self->getSkin($form->{section});
+
+
+	if ($tabtype eq 'tabsection') {
+		$form->{fhfilter} = "story";
+		$options->{orderdir} = "DESC";
+		$options->{orderby} = "createtime";
+		$options->{color} = "black";
+	} elsif ($tabtype eq 'tabrecent') {
+		$form->{fhfilter} = "-story";
+		$options->{orderby} = "createtime";
+		$options->{orderdir} = "DESC";
+		$options->{color} = "blue";
+	} elsif ($tabtype eq 'tabpopular') {
+		$form->{fhfilter} = "-story";
+		$options->{orderby} = "popularity";
+		$options->{orderdir} = "DESC";
+		$options->{color} = "black";
+	} elsif ($tabtype eq 'tabuser') {
+		$form->{fhfilter} = "user:";
+		$options->{orderby} = "popularity";
+		$options->{color} = "black";
+		$options->{orderdir} = "DESC";
+		$options->{orderby} = "createtime";
+	}
+
+	if ($tabtype) {
+		$form->{fhfilter} = "$the_skin->{name} $form->{fhfilter}" if $the_skin->{skid} != $constants->{mainpage_skid};
+	}
+
+
 	if (defined $form->{fhfilter}) {
 		$fhfilter = $form->{fhfilter};
 		$options->{fhfilter} = $fhfilter;
@@ -1730,37 +1779,10 @@
 		$options->{fhfilter} = $fhfilter;
 	}
 
-	# XXX
 	my $user_tabs = $self->getUserTabs();
 	my %user_tab_names = map { $_->{tabname} => 1 } @$user_tabs;
-	my $tabs_given = $user->{firehose_tabs_given} || '';
-	my %firehose_tabs_given = map { $_ => 1 } split (/\|/, $tabs_given);
 	my @tab_fields = qw(tabname filter mode color orderdir orderby);
 
-	my $system_tabs = $self->getSystemDefaultTabs();
-	foreach my $tab (@$system_tabs) {
-		my $data = {};
-		foreach (@tab_fields) {
-			$data->{$_} = $tab->{$_};
-			
-			if ($tab->{$_} eq "User" && $_ eq "tabname") {
-				$data->{$_} = $user->{nickname};
-				$data->{$_} =~ s/[^A-Za-z0-9_-]//g;
-				if(length $data->{$_} > 16) {
-					$data->{$_} = substr($data->{$_}, 0, 16);
-				}
-				$data->{$_} = "User" if length($data->{$_}) == 0;
-			}
-			foreach my $field (qw(uid nickname)) {
-				$data->{$_} =~ s/{$field}/$user->{$field}/g;
-			}
-		}
-		if (!$user_tab_names{$tab->{tabname}} && !$firehose_tabs_given{$tab->{tabname}} && !$user->{is_anon}) {
-			$self->createUserTab($user->{uid}, $data); 
-			$tabs_given .= $tab->{tabname} ."|";
-			$self->setUser($user->{uid}, { firehose_tabs_given => $tabs_given });
-		}
-	}
 	$user_tabs = $self->getUserTabs();
 
 
@@ -1770,19 +1792,37 @@
 		filter 		=> "fhfilter" 
 	};
 
+	my $skin_prefix="";
+	if ($the_skin && $the_skin->{name} && $the_skin->{skid} != $constants->{mainpage_skid})  {
+		$skin_prefix = "$the_skin->{name} ";
+	}
+	my $system_tabs = [ 
+		{ tabtype => 'tabsection', color => 'black', filter => $skin_prefix . "story"},
+		{ tabtype => 'tabpopular', color => 'black', filter => "$skin_prefix\-story"},
+		{ tabtype => 'tabrecent',  color => 'blue',  filter => "$skin_prefix\-story"},
+	];
+
+	if (!$user->{is_anon}) {
+		push @$system_tabs, { tabtype => 'tabuser', color => 'black', filter => $skin_prefix . "user:"};
+	}
+
+	my $sel_tabtype;
+
 	my $tab_match = 0;
-	foreach my $tab (@$user_tabs) {
+	foreach my $tab (@$user_tabs, @$system_tabs) {
 		my $equal = 1;
 		foreach (keys %$tab_compare) {
 			$options->{$tab_compare->{$_}} ||= "";
 			if ($tab->{$_} ne $options->{$tab_compare->{$_}}) {
 				$equal = 0;
-				#print STDERR "$tab->{tabname} -> $_ doesn't match\n";
 			}
 		}
 		if ($equal) {
 			$tab_match = 1;
 			$tab->{active} = 1;
+			if (defined $tab->{tabtype}) {
+				$sel_tabtype = $tab->{tabtype};
+			}
 			
 			# Tab match if new option is being set update tab
 			if ($form->{orderdir} || $form->{orderby} || $form->{mode}) {
@@ -1894,6 +1934,7 @@
 			$fhfilter .= " $gSkin->{name}";
 		}
 	}
+
 	my $fh_ops = $self->splitOpsFromString($fhfilter);
 	
 
@@ -1994,7 +2035,7 @@
 	if (!$user->{is_anon} && !$opts->{no_set} && !$form->{index}) {
 		my $data_change = {};
 		my @skip_options_save = qw(uid not_uid type not_type primaryskid not_primaryskid smalldevices);
-		if ($user->{state}{firehose_page} eq "user") {
+		if ($firehose_page eq 'user') {
 			push @skip_options_save, "nothumbs", "nocolors", "pause", "mode", "orderdir", "orderby", "fhfilter", "color";
 		}
 		my %skip_options = map { $_ => 1 } @skip_options_save;
@@ -2006,6 +2047,7 @@
 	}
 
 	$options->{tabs} = $user_tabs;
+	$options->{sel_tabtype} = $sel_tabtype;
 
 	if ($user->{is_admin} && $form->{setusermode}) {
 		$options->{firehose_usermode} = $form->{firehose_usermode} ? 1 : "";
@@ -2024,13 +2066,13 @@
 	$options->{public} = "yes";
 	if ($adminmode) {
 		# $options->{attention_needed} = "yes";
-		if ($user->{state}{firehose_page} ne "user") {
+		if ($firehose_page ne "user") {
 			$options->{accepted} = "no" if !$options->{accepted};
 			$options->{rejected} = "no" if !$options->{rejected};
 		}
 		$options->{duration} ||= -1;
 	} else  {
-		if ($user->{state}{firehose_page} ne "user") {
+		if ($firehose_page ne "user") {
 			$options->{accepted} = "no" if !$options->{accepted};
 		}
 		
@@ -2148,6 +2190,8 @@
 	my $slashdb = getCurrentDB();
 	my $user = getCurrentUser();
 	my $gSkin = getCurrentSkin();
+	my $form = getCurrentForm();
+
 	my $firehose_reader = getObject('Slash::FireHose', {db_type => 'reader'});
 	my $featured;
 
@@ -2157,7 +2201,8 @@
 			$featured = $firehose_reader->getFireHose($res->[0]->{id});
 		}
 	}
-	my $options = $lv_opts->{options} || $self->getAndSetOptions();
+	my $initial = ($form->{tab} || $form->{tabtype} || $form->{fhfilter}) ? 0 : 1;
+	my $options = $lv_opts->{options} || $self->getAndSetOptions({ initial => $initial });
 	my $base_page = $lv_opts->{fh_page} || "firehose.pl";
 
 	if ($featured && $featured->{id}) {
@@ -2226,6 +2271,12 @@
 	} else {
 		$refresh_options->{insert_new_at} = "top";
 	}
+	
+	my $section = 0;
+	if ($gSkin->{skid} != $constants->{mainpage_skid}) {
+		$section = $gSkin->{skid};
+	}
+
 	slashDisplay("list", {
 		itemstext	=> $itemstext, 
 		itemnum		=> $itemnum,
@@ -2241,6 +2292,7 @@
 		fh_page		=> $base_page,
 		search_results	=> $results,
 		featured	=> $featured,
+		section		=> $section
 	}, { Page => "firehose", Return => 1 });
 }
 
@@ -2273,7 +2325,7 @@
 	push @where, "tabname LIKE '$options->{prefix}%'" if $options->{prefix};
 	my $where = join ' AND ', @where;
 
-	my $tabs = $self->sqlSelectAllHashrefArray("*", "firehose_tab", $where, "order by tabname asc");
+	my $tabs = $self->sqlSelectAllHashrefArray("*", "firehose_tab", $where, "ORDER BY tabname ASC");
 	@$tabs = sort { 
 			$b->{tabname} eq "untitled" ? -1 : 
 				$a->{tabname} eq "untitled" ? 1 : 0	||
@@ -2342,13 +2394,20 @@
 	my @retitems;
 	my $last_day = "00000000";
 	my $days_processed = 0;
+	my $last_days_processed = 0;
 	foreach (@$items) {
 		my $cur_day = $_->{createtime};
 		$cur_day =  timeCalc($cur_day, "%Y%m%d %T", $offset);
 		$cur_day =~ s/ \d\d:\d\d:\d\d$//g;
-		if ($cur_day ne $last_day && $days_processed >= 3) {
-			push @retitems, { id => "day-$cur_day", day => $cur_day, last_day => $last_day };
+		if ($cur_day ne $last_day) {
+			if ($last_days_processed >= 5) {
+				push @retitems, { id => "day-$cur_day", day => $cur_day, last_day => $last_day };
+			}
+			$last_days_processed = 0;
+		} else {
+			$last_days_processed++;
 		}
+		
 		push @retitems, $_;
 		$last_day = $cur_day;
 		$days_processed++;
@@ -2432,6 +2491,27 @@
 	return $self->sqlSelectAllHashrefArray("firehose.id,urls.url", "firehose,urls", "firehose.type='submission' AND firehose.url_id=urls.url_id AND mediatype='video' $lastid", "ORDER BY firehose.id ASC $limit");
 }
 
+sub createSectionSelect {
+	my($self, $default) = @_;
+	my $skins = $self->getSkins();
+	my $constants = getCurrentStatic();
+	my $ordered = [];
+	my $menu;
+
+	foreach my $skid (keys %$skins) {
+		if ($skins->{$skid}{skid} == $constants->{mainpage_skid}) {
+			$menu->{0} = $constants->{sitename};
+		} else {
+			$menu->{$skid} = $skins->{$skid}{title};
+		}
+	}
+
+	@$ordered = sort {$a == 0 ? -1 : $b == 0 ? 1 : 0 || $menu->{$a} cmp $menu->{$b} } keys %$menu;
+	return createSelect("fh_section", $menu, { default => $default, return => 1, nsort => 0, ordered => $ordered, multiple => 0, onchange =>"firehose_set_options('tabsection', this.options[this.selectedIndex].value)"});
+
+	
+}
+
 1;
 
 __END__
@@ -2443,4 +2523,4 @@
 
 =head1 VERSION
 
-$Id: FireHose.pm,v 1.197 2008/01/09 20:04:51 jamiemccarthy Exp $
+$Id: FireHose.pm,v 1.199 2008/01/18 21:18:20 tvroom Exp $

Modified: slashjp/branches/upstream/current/plugins/FireHose/firehose.pl
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/firehose.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/firehose.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: firehose.pl,v 1.45 2007/11/15 17:30:39 scc Exp $
+# $Id: firehose.pl,v 1.47 2008/01/18 22:00:45 tvroom Exp $
 
 use strict;
 use warnings;
@@ -14,7 +14,7 @@
 use Slash::XML;
 use vars qw($VERSION);
 
-($VERSION) = ' $Revision: 1.45 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.47 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 
 sub main {
@@ -27,9 +27,9 @@
 	my $anonval = $constants->{firehose_anonval_param} || "";
 
 	my %ops = (
-		list		=> [1,  \&list, 1, $anonval, { index => 1, issue => 1, page => 1, query_apache => -1, virtual_user => -1, startdate => 1, duration => 1 }],
+		list		=> [1,  \&list, 1, $anonval, { index => 1, issue => 1, page => 1, query_apache => -1, virtual_user => -1, startdate => 1, duration => 1, tab => 1, tabtype => 1 }],
 		view		=> [1, 	\&view, 0,  ""],
-		default		=> [1,	\&list, 1,  $anonval, { index => 1, issue => 1, page => 1, query_apache => -1, virtual_user => -1, startdate => 1, duration => 1 }],
+		default		=> [1,	\&list, 1,  $anonval, { index => 1, issue => 1, page => 1, query_apache => -1, virtual_user => -1, startdate => 1, duration => 1, tab => 1, tabtype => 1 }],
 		edit		=> [1,	\&edit, 100,  ""],
 		rss		=> [1,  \&rss, 1, ""]
 	);
@@ -108,7 +108,7 @@
 			$firehose->setFireHoseSession($item->{id});
 		}
 		my $tags_top = $firehose_reader->getFireHoseTagsTop($item);
-		my $discussion = $item->{type} =~ /^submission|misc$/ && $item->{discussion};
+		my $discussion = $item->{discussion};
 
 		my $firehosetext = $firehose_reader->dispFireHose($item, {
 			mode			=> 'full',

Modified: slashjp/branches/upstream/current/plugins/FireHose/firehose_get_thumbnails.pl
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/firehose_get_thumbnails.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/firehose_get_thumbnails.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: firehose_get_thumbnails.pl,v 1.3 2007/11/01 02:01:04 tvroom Exp $
+# $Id: firehose_get_thumbnails.pl,v 1.4 2008/01/16 22:24:24 jamiemccarthy Exp $
 
 use strict;
 
@@ -39,7 +39,7 @@
 		my ($scheme, $domain, $path, $query, $frag) = uri_split($_->{url});
 		my $page = get $_->{url};
 		slashdLog("$_->{id}: $_->{url}\n");
-		my @pairs = split(/&/, $query);
+		my @pairs = split /&/, ($query || '');
 		my $params = {};
 		foreach my $pair (@pairs) {
 			my ($name, $value) = split(/=/, $pair);

Modified: slashjp/branches/upstream/current/plugins/FireHose/firehose_reject_old.pl
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/firehose_reject_old.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/firehose_reject_old.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: firehose_reject_old.pl,v 1.2 2007/08/15 03:07:29 tvroom Exp $
+# $Id: firehose_reject_old.pl,v 1.3 2008/01/16 20:28:18 tvroom Exp $
 
 use strict;
 
@@ -22,7 +22,7 @@
 $task{$me}{code} = sub {
 	my($virtual_user, $constants, $slashdb, $user, $info, $gSkin) = @_;
 	my $firehose = getObject("Slash::FireHose");
-	my $old = $slashdb->sqlSelectColArrayref("id", "firehose", "createtime < DATE_SUB(NOW(),INTERVAL 7 DAY) and category ='' and rejected='no'");
+	my $old = $slashdb->sqlSelectColArrayref("id", "firehose", "createtime < DATE_SUB(NOW(),INTERVAL 7 DAY) and type !='story' AND category ='' and rejected='no'");
 	foreach (@$old) {
 		$firehose->reject($_);
 	}

Modified: slashjp/branches/upstream/current/plugins/FireHose/templates/firehose_tabs;misc;default
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/templates/firehose_tabs;misc;default	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/templates/firehose_tabs;misc;default	2008-01-22 10:59:02 UTC (rev 449)
@@ -14,6 +14,7 @@
 __seclev__
 10000
 __template__
+[% fh = Slash.getObject("Slash::FireHose"); %]
 [% rss_options = { "fhfilter" => "filter", "orderdir" => "orderdir", "orderby" => "orderby", color => "color", "duration" => "duration" } %]
 [% rss_param_str = "" %]
 [% FOREACH param = rss_options.keys;
@@ -30,6 +31,12 @@
 		 END;
 		Slash.createSelect('tab', tabnames, { default => tab_current, return => 1, onchange => "firehose_set_options('tab', this.options[this.selectedIndex].value])"});
 	 ELSE %]
+	 	<li id="fhtab-section" [% IF options.sel_tabtype == "tabsection" %]class="active"[% END %]>[% fh.createSectionSelect(section) %]</li>
+	 	<li id="fhtab-recent" [% IF options.sel_tabtype == "tabrecent" %]class="active"[% END %]><a href="?tabtype=tabrecent" onclick="firehose_set_options('tabtype','tabrecent'); return false">Recent</a></li>
+	 	<li id="fhtab-popular" [% IF options.sel_tabtype == "tabpopular" %]class="active"[% END %]><a href="?tabtype=tabpopular" onclick="firehose_set_options('tabtype','tabpopular'); return false">Popular</a></li>
+		[% IF !user.is_anon %]
+	 	<li id="fhtab-user" [% IF options.sel_tabtype == "tabuser" %]class="active"[% END %]><a href="?tabtype=tabuser" onclick="firehose_set_options('tabtype','tabuser'); return false">[% user.nickname | strip_literal %]</a></li>
+		[% END %]
 	[% FOREACH tab = tabs %]
 		<li id="fhtab-[% tab.tabid %]"[% IF tab.active %] class="active"[% END %]>
 		[% IF tab.active %]<span id="tab-form-[% tab.tabid %]" class="hide"><input type="text" onfocus="focusCompleter(this, '[% tab.tabid %]', '[% user.is_admin && !user.firehose_usermode %]','firehosetab', 7, { yui:{minQueryLength:0, autoHighlight:false }, action1:completer_save_tab } )" id="tab-input-[% tab.tabid %]" size="12" value="[% tab.tabname | strip_literal %]"></span>[% END %]
@@ -40,4 +47,4 @@
 	[% END %]
 [% UNLESS nodiv %]</ul>[% END %]
 __version__
-$Id: firehose_tabs;misc;default,v 1.19 2007/08/22 20:26:14 scc Exp $
+$Id: firehose_tabs;misc;default,v 1.20 2008/01/18 21:19:57 tvroom Exp $

Modified: slashjp/branches/upstream/current/plugins/FireHose/templates/list;firehose;default
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/templates/list;firehose;default	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/templates/list;firehose;default	2008-01-22 10:59:02 UTC (rev 449)
@@ -46,6 +46,7 @@
 [% ua = env.HTTP_USER_AGENT %]
 
 <div id="slashboxes">
+[% Slash.getAd(2) %]
 [% UNLESS user.noboxes %]
   [% IF slashboxes && !(form.smalldevices || form.embed || (constants.smalldevices_ua_regex && ua.match(constants.smalldevices_ua_regex)));
     slashboxes %]
@@ -90,7 +91,7 @@
 <div id="message_area">
 </div>
 	[% IF !user.is_anon %]
-		[% PROCESS firehose_tabs tabs = options.tabs options = options %]
+		[% PROCESS firehose_tabs tabs = options.tabs options = options section=section %]
                 <div class="advpref" align="right"><a href="#" id="fh_adv_pref_toggle" onclick="firehose_toggle_advpref(); return false" title="Firehose preferences"></a></div>
 		[% PROCESS adv_pref_firehose options = options %]
 	[% END %]
@@ -144,6 +145,7 @@
 </p>
 [% END %]
 [% END # form.skipmenu %]
+[% Slash.getAd(6) %]
 <div id="firehoselist">
 [% itemstext %]
 [% page = page || "0" %]
@@ -156,6 +158,7 @@
 	firehose_settings.startdate = '[% options.startdate.replace('-','') %]';
 	firehose_settings.issue = '[% options.issue %]';
 	firehose_settings.duration = '[% options.duration %]';
+	firehose_settings.section = '[% section %]';
 	[% IF fh_page == "console.pl" %]
 		fh_pageval = 1;
 	[% ELSIF fh_page == "users.pl" %]
@@ -199,4 +202,4 @@
 </script>
 
 __version__
-$Id: list;firehose;default,v 1.107 2008/01/09 18:26:57 scc Exp $
+$Id: list;firehose;default,v 1.108 2008/01/18 21:19:57 tvroom Exp $

Modified: slashjp/branches/upstream/current/plugins/FireHose/templates/view;firehose;default
===================================================================
--- slashjp/branches/upstream/current/plugins/FireHose/templates/view;firehose;default	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/FireHose/templates/view;firehose;default	2008-01-22 10:59:02 UTC (rev 449)
@@ -13,13 +13,20 @@
 __seclev__
 10000
 __template__
-[% PROCESS autocomplete %]
-[% PROCESS nodnix_menus %]
-[% IF user.is_anon;
-   	this_fhid = form.id | fixparam;
-   	PROCESS userlogin_cover
+<div class="view">
+	[% PROCESS autocomplete %]
+	[% PROCESS nodnix_menus %]
+	[% IF user.is_anon;
+	   	this_fhid = form.id | fixparam;
+	   	PROCESS userlogin_cover
    		return_url = gSkin.rootdir _ '/firehose.pl?op=view&id=' _ this_fhid;
-END %]
-[% firehosetext %]
+	END %]
+	<div class="head">
+       		<div class="yui-b">
+		<!-- block -->
+		</div>
+		[% firehosetext %]
+	</div>
+</div>
 __version__
-$Id: view;firehose;default,v 1.8 2007/10/11 22:14:06 pudge Exp $
+$Id: view;firehose;default,v 1.9 2008/01/18 21:20:37 tvroom Exp $

Modified: slashjp/branches/upstream/current/plugins/HumanConf/Static/Static.pm
===================================================================
--- slashjp/branches/upstream/current/plugins/HumanConf/Static/Static.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/HumanConf/Static/Static.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Static.pm,v 1.41 2007/10/25 17:11:15 jamiemccarthy Exp $
+# $Id: Static.pm,v 1.42 2008/01/18 22:36:50 pudge Exp $
 
 package Slash::HumanConf::Static;
 
@@ -19,7 +19,7 @@
 use base 'Slash::DB::Utility';
 use base 'Slash::DB::MySQL';
 
-($VERSION) = ' $Revision: 1.41 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.42 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 sub new {
 	my($class, $user) = @_;
@@ -367,7 +367,7 @@
 	system("swift -f $ssml_file -o $wav_file");
 	unlink($ssml_file);
 	if ($constants->{hc_cepstral_mp3encoder}) {
-		system("$constants->{hc_cepstral_mp3encoder} -S --resample 22.05 $wav_file $full_filename_mp3");
+		system("TERM=dumb $constants->{hc_cepstral_mp3encoder} -S --resample 22.05 $wav_file $full_filename_mp3");
 	}
 	unlink($wav_file);
 	return $filename_mp3;

Modified: slashjp/branches/upstream/current/plugins/Stats/Stats.pm
===================================================================
--- slashjp/branches/upstream/current/plugins/Stats/Stats.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Stats/Stats.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Stats.pm,v 1.187 2008/01/07 16:15:52 jamiemccarthy Exp $
+# $Id: Stats.pm,v 1.190 2008/01/21 15:49:48 jamiemccarthy Exp $
 
 package Slash::Stats;
 
@@ -22,7 +22,7 @@
 use base 'Slash::DB::Utility';
 use base 'Slash::DB::MySQL';
 
-($VERSION) = ' $Revision: 1.187 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.190 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 sub new {
 	my($class, $user, $options) = @_;
@@ -95,7 +95,8 @@
 
 		$self->sqlDo("DROP TABLE IF EXISTS accesslog_temp_host_addr");
 		$self->sqlDo("DROP TABLE IF EXISTS accesslog_build_unique_uid");
-		$self->sqlDo("CREATE TABLE accesslog_temp_host_addr (host_addr char(32) UNIQUE NOT NULL, anon ENUM('no','yes') NOT NULL DEFAULT 'yes', PRIMARY KEY (host_addr, anon)) TYPE = InnoDB");
+		$self->sqlDo("CREATE TABLE accesslog_temp_host_addr (host_addr char(32) NOT NULL, anon ENUM('no','yes') NOT NULL DEFAULT 'yes', PRIMARY KEY (host_addr, anon)) TYPE = InnoDB");
+		$self->sqlDo("CREATE TABLE accesslog_temp_uidip (uidip varchar(32) NOT NULL, op varchar(254) NOT NULL, PRIMARY KEY (uidip, op)) TYPE = InnoDB");
 		$self->sqlDo("CREATE TABLE accesslog_build_unique_uid ( uid MEDIUMINT UNSIGNED NOT NULL, PRIMARY KEY (uid)) TYPE = InnoDB");
 
 		# Then, get the schema in its CREATE TABLE statement format.
@@ -221,6 +222,24 @@
 			{ ignore => 1 } 
 			);
 
+		return undef unless $self->_do_insert_select(
+			"accesslog_temp_uidip",
+			"IF(uid = $constants->{anonymous_coward_uid}, uid, host_addr), op",
+			"accesslog_temp",
+			"",
+			3, 60, 
+			{ ignore => 1 }
+			);
+	
+		return undef unless $self->_do_insert_select(
+			"accesslog_temp_uidip",
+			"IF(uid = $constants->{anonymous_coward_uid}, uid, host_addr), op",
+			"accesslog_temp_rss",
+			"",
+			3, 60, 
+			{ ignore => 1 }
+			);
+	
 
 	}
 
@@ -1079,7 +1098,42 @@
 		$where);
 }
 
+# $hit_ar and $nohit_ar are hashrefs which indicate which op
+# combinations to check.  For example:
+#	$hit_ar = [ 'index', 'rss' ];
+#	$nohit_ar = [ 'article', 'comments' ];
+# will return the number of IP-uid combinations which logged one
+# or more hits with op=index, one or more hits with op=rss, and
+# zero hits for both op=article and op=comments hits.
+#
+# This is a logical AND, so you can think of the test as being:
+# 	uidip hit w AND hit x AND NOT hit y AND NOT hit z
 
+sub getOpCombinationStats {
+	my($self, $hit_ar, $nohit_ar) = @_;
+
+	my @tables = ( );
+	my @where = ( );
+	my $tn = 0;
+	for my $hit (@$hit_ar) {
+		++$tn;
+		push @tables, "accesslog_temp_uidip AS a$tn";
+		push @where, "a$tn.uidip = a1.uidip" if $tn > 1;
+		push @where, "a$tn.op=" . $self->sqlQuote($hit);
+	}
+	if (@$nohit_ar) {
+		++$tn;
+		my $notlist = join ',', map { $self->sqlQuote($_) } @$nohit_ar;
+		$tables[-1] .= " LEFT JOIN accesslog_temp_uidip AS a$tn
+			ON (a$tn.uidip=a1.uidip AND a$tn.op IN ($notlist))";
+		push @where, "a$tn.op IS NULL";
+	}
+	my $tables = join ', ', @tables;
+	my $where  = join ' AND ', @where;
+
+	return $self->sqlSelect('COUNT(DISTINCT a1.uidip)', $tables, $where);
+}
+
 ########################################################
 sub getSectionSummaryStats {
 	my ($self, $options) = @_;
@@ -1704,13 +1758,13 @@
 sub getTopBadgeHosts {
 	my($self, $options) = @_;
 	my $count = $options->{count} || 10;
-	my $url_count = $count * 20;
+	my $url_count = $count * 40;
 	my $top_ar = $self->getTopBadgeURLs({ count => $url_count });
 	my %count = ( );
 	for my $duple (@$top_ar) {
 		my($uri, $c) = @$duple;
 		my $uri_obj = URI->new($uri);
-		my $host = $uri_obj ? $uri_obj->host() : $uri;
+		my $host = $uri_obj && $uri_obj->can('host') ? $uri_obj->host() : $uri;
 		$count{$host} += $c;
 	}
 	my @top_hosts = (sort { $count{$b} <=> $count{$a} || $a <=> $b } keys %count);
@@ -1719,7 +1773,17 @@
 	return \@top;
 }
 
-########################################################
+sub getNumBookmarks {
+	my($self, $options) = @_;
+	my $constants = getCurrentStatic();
+	my $anon_clause = '';
+	$anon_clause = " AND uid=$constants->{anonymous_coward_uid}" if $options->{anon_only};
+	return $self->sqlCount('bookmark_id',
+		'bookmarks',
+		"createdtime $self->{_day_between_clause} $anon_clause");
+}
+
+#######################################################
 sub countSfNetIssues {
 	my($self, $group_id) = @_;
 	my $constants = getCurrentStatic();
@@ -2148,4 +2212,4 @@
 
 =head1 VERSION
 
-$Id: Stats.pm,v 1.187 2008/01/07 16:15:52 jamiemccarthy Exp $
+$Id: Stats.pm,v 1.190 2008/01/21 15:49:48 jamiemccarthy Exp $

Modified: slashjp/branches/upstream/current/plugins/Stats/adminmail.pl
===================================================================
--- slashjp/branches/upstream/current/plugins/Stats/adminmail.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Stats/adminmail.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: adminmail.pl,v 1.216 2007/11/29 23:26:30 jamiemccarthy Exp $
+# $Id: adminmail.pl,v 1.217 2008/01/21 15:49:48 jamiemccarthy Exp $
 
 use strict;
 use Slash::Constants qw( :messages :slashd );
@@ -821,7 +821,15 @@
 	$data{top_referers} = $logdb->getTopReferers({count => 20});
 	$data{top_badgehosts} = $logdb->getTopBadgeHosts({count => 20});
 	$data{top_badgeurls} = $logdb->getTopBadgeURLs({count => 20});
+	for my $host_duple (@{ $data{top_badgehosts} }) {
+		my($host, $count) = @$host_duple;
+		$host =~ s/\W+/_/g;
+		$statsSave->createStatDaily("badgehost_$host", $count);
+	}
 
+	$data{bookmarks_anon} = $logdb->getNumBookmarks({ anon_only => 1 });
+	$statsSave->createStatDaily("bookmarks_anon", $data{bookmarks_anon});
+
 	my $new_users_yest = $slashdb->getNumNewUsersSinceDaysback(1)
 		- $slashdb->getNumNewUsersSinceDaysback(0);
 	$statsSave->createStatDaily('users_created', $new_users_yest);
@@ -861,7 +869,7 @@
 	}
 
 	if ($firehose) {
-		my $fh_report;
+		my $fh_report = '';
 		my $fh_nods_nix = $stats->getTagCountForDay($yesterday, ["nod", "nix" ]);
 		$statsSave->createStatDaily("firehose_nod_nix", $fh_nods_nix);
 		$fh_report .= "Firehose total nods & nixes: $fh_nods_nix\n";
@@ -894,7 +902,7 @@
 	}
 
 	if ($tags) {
-		my $tags_report;
+		my $tags_report = '';
 		my $tags_users = $stats->getTagCountForDay($yesterday, [], 1);
 		$statsSave->createStatDaily("tags_users", $tags_users);
 		$tags_report .= "Tags active users: $tags_users\n";

Modified: slashjp/branches/upstream/current/plugins/Stats/templates/display;adminmail;default
===================================================================
--- slashjp/branches/upstream/current/plugins/Stats/templates/display;adminmail;default	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Stats/templates/display;adminmail;default	2008-01-22 10:59:02 UTC (rev 449)
@@ -60,7 +60,7 @@
 [%- FOREACH pagetype = extra_pagetypes -%]
 [% pt_uids = pagetype _ "_uids"; pt_ipids = pagetype _ "_ipids" ; pt_page = pagetype _ "_page" ; pt_bytes = pagetype _ "_bytes"; pt_label = pagetype _ "_label"; -%]
 [% IF $pt_page > 0 -%]
-     [% $pt_label %]: [% $pt_uids %]   [% $pt_ipids %]   [% $pt_page %] ([% $pt_bytes %])
+   [% $pt_label %]: [% $pt_uids %]   [% $pt_ipids %]   [% $pt_page %] ([% $pt_bytes %])
 [% END -%]
 [% END %]
 
@@ -231,4 +231,4 @@
 __seclev__
 100
 __version__
-$Id: display;adminmail;default,v 1.85 2007/11/29 23:26:30 jamiemccarthy Exp $
+$Id: display;adminmail;default,v 1.86 2008/01/18 01:55:16 jamiemccarthy Exp $

Modified: slashjp/branches/upstream/current/plugins/Tags/Tags.pm
===================================================================
--- slashjp/branches/upstream/current/plugins/Tags/Tags.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Tags/Tags.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -1,7 +1,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Tags.pm,v 1.96 2008/01/09 20:04:51 jamiemccarthy Exp $
+# $Id: Tags.pm,v 1.97 2008/01/18 21:28:17 jamiemccarthy Exp $
 
 package Slash::Tags;
 
@@ -17,7 +17,7 @@
 use base 'Slash::DB::Utility';
 use base 'Slash::DB::MySQL';
 
-($VERSION) = ' $Revision: 1.96 $ ' =~ /\$Revision:\s+([^\s]+)/;
+($VERSION) = ' $Revision: 1.97 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 # FRY: And where would a giant nerd be? THE LIBRARY!
 
@@ -190,9 +190,8 @@
 		# a tag_clout in tagname_params.
 		my $admincmds_ar = $self->getTagnameAdmincmds(
 			$tag->{tagnameid}, $tag->{globjid});
-		# Any admin command other than '^' means clout must be set
-		# to 0.
-		if (grep { $_->{cmdtype} ne '^' } @$admincmds_ar) {
+		# Any negative admin command means clout must be set to 0.
+		if (grep { $_->{cmdtype} =~ /^[_#]/ } @$admincmds_ar) {
 			my $count = $self->sqlInsert('tag_params', {
 				tagid =>	$tagid,
 				name =>		'tag_clout',

Modified: slashjp/branches/upstream/current/plugins/Tags/tags_udc.pl
===================================================================
--- slashjp/branches/upstream/current/plugins/Tags/tags_udc.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/plugins/Tags/tags_udc.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: tags_udc.pl,v 1.5 2007/09/28 02:08:03 jamiemccarthy Exp $
+# $Id: tags_udc.pl,v 1.6 2008/01/18 21:27:52 jamiemccarthy Exp $
 
 # Tags Upvote/Downvote Count
 #
@@ -21,11 +21,12 @@
 use Slash::Utility;
 use Slash::Constants ':slashd';
 
-(my $VERSION) = ' $Revision: 1.5 $ ' =~ /\$Revision:\s+([^\s]+)/;
+(my $VERSION) = ' $Revision: 1.6 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 $task{$me}{timespec} = '2-59/5 * * * *';
 $task{$me}{timespec_panic_1} = ''; # not that important
 $task{$me}{fork} = SLASHD_NOWAIT;
+$task{$me}{on_startup} = 1;
 
 $task{$me}{code} = sub {
 	my($virtual_user, $constants, $slashdb, $user) = @_;

Modified: slashjp/branches/upstream/current/sbin/portald
===================================================================
--- slashjp/branches/upstream/current/sbin/portald	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/sbin/portald	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: portald,v 1.51 2008/01/09 18:07:20 entweichen Exp $
+# $Id: portald,v 1.52 2008/01/12 05:11:35 pudge Exp $
 
 ###############################################################################
 # portald  - this is the "daemon" responsible for retrieving portal and site
@@ -275,7 +275,7 @@
 
 $slashdb->setCurrentSectionPolls();
 
-getTop10Comments();
+getTopComments();
 getSlashdotPoll();
 getUptime();
 

Modified: slashjp/branches/upstream/current/sql/mysql/defaults.sql
===================================================================
--- slashjp/branches/upstream/current/sql/mysql/defaults.sql	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/sql/mysql/defaults.sql	2008-01-22 10:59:02 UTC (rev 449)
@@ -3,7 +3,7 @@
 #--------------------------------------------------------
 # Server version	3.23.26-beta-log
 #
-# $Id: defaults.sql,v 1.385 2008/01/09 21:02:35 pudge Exp $
+# $Id: defaults.sql,v 1.386 2008/01/21 19:33:56 pudge Exp $
 #
 
 #
@@ -832,7 +832,7 @@
 INSERT INTO vars (name, value, description) VALUES ('cur_performance_stats_lastid', '0', 'accesslogid to start searching at');
 INSERT INTO vars (name, value, description) VALUES ('cur_performance_stats_weeks', '8', 'number of weeks back to compare current stats to');
 INSERT INTO vars (name, value, description) VALUES ('currentqid',1,'The Current Question on the homepage pollbooth');
-INSERT INTO vars (name, value, description) VALUES ('cvs_tag_currentcode','T_2_5_0_189','The current cvs tag that the code was updated to - this does not affect site behavior but may be useful for your records');
+INSERT INTO vars (name, value, description) VALUES ('cvs_tag_currentcode','T_2_5_0_190','The current cvs tag that the code was updated to - this does not affect site behavior but may be useful for your records');
 INSERT INTO vars (name, value, description) VALUES ('datadir','/usr/local/slash/www.example.com','What is the root of the install for Slash');
 INSERT INTO vars (name, value, description) VALUES ('db_auto_increment_increment','1','If your master DB uses auto_increment_increment, i.e. multiple master replication, echo its value into this var');
 INSERT INTO vars (name, value, description) VALUES ('dbsparklines_disp','0','Display dbsparklines in the currentAdminUsers box?');

Modified: slashjp/branches/upstream/current/sql/mysql/upgrades
===================================================================
--- slashjp/branches/upstream/current/sql/mysql/upgrades	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/sql/mysql/upgrades	2008-01-22 10:59:02 UTC (rev 449)
@@ -10,7 +10,7 @@
 #      after X started at the same time that X was tagged.
 
 #
-# $Id: upgrades,v 1.1297 2008/01/09 21:02:36 pudge Exp $
+# $Id: upgrades,v 1.1299 2008/01/21 19:33:56 pudge Exp $
 #
 
 # BEGIN tf23's additions 
@@ -5077,8 +5077,6 @@
 # for plugins/Metamod
 INSERT INTO vars (name, value, description) VALUES ('m2_only_perdec', '10', 'What perdecage of mods should get M2d? 1=1/10th, 10=all');
 
-# SLASHDOT LAST UPDATED HERE
-
 # for plugins/Tags
 # perl -MSlash::Test=virtusername -le 'map { $tags->setTagname($_, { exclude => 1 }) } map { $tags->getTagnameidCreate($_) } split / /, $constants->{tagbox_top_excludetagnames}; map { $tags->setTagname($_, { posneg => "-" }) } map { $tags->getTagnameidCreate($_) } split ",", $constants->{tags_negative_tagnames}; $tags->setTagname($tags->getTagnameidCreate("nod"), { posneg => "+" }); map { $tags->setTagname($_, { popup => 1, posneg => "+" }) } map { $tags->getTagnameidCreate($_) } qw(interesting fresh funny insightful); map { $tags->setTagname($_, { popup => 1, posneg => "-" }) } map { $tags->getTagnameidCreate($_) } qw(binspam dupe notthebest offtopic stupid slownewsday stale)'
 DELETE FROM vars WHERE name IN ('tagbox_top_excludetagnames', 'tags_negative_tagnames');
@@ -5086,8 +5084,6 @@
 # 2007-12-20
 UPDATE vars SET value = 'T_2_5_0_188' WHERE name = 'cvs_tag_currentcode';
 
-# SLASHCODE/USEPERL LAST UPDATED HERE
-
 ALTER TABLE accesslog CHANGE query_string query_string VARCHAR(254) DEFAULT NULL;
 
 # For portald and the Hot Comments slashbox.
@@ -5101,3 +5097,10 @@
 # 2007-01-09
 UPDATE vars SET value = 'T_2_5_0_189' WHERE name = 'cvs_tag_currentcode';
 
+# SLASHCODE/USEPERL LAST UPDATED HERE
+
+# SLASHDOT LAST UPDATED HERE
+
+# 2007-01-21
+UPDATE vars SET value = 'T_2_5_0_190' WHERE name = 'cvs_tag_currentcode';
+

Modified: slashjp/branches/upstream/current/tagboxes/Despam/Despam.pm
===================================================================
--- slashjp/branches/upstream/current/tagboxes/Despam/Despam.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/tagboxes/Despam/Despam.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: Despam.pm,v 1.5 2008/01/07 16:17:56 jamiemccarthy Exp $
+# $Id: Despam.pm,v 1.6 2008/01/21 15:53:42 jamiemccarthy Exp $
 
 package Slash::Tagbox::Despam;
 
@@ -28,7 +28,7 @@
 use Data::Dumper;
 
 use vars qw( $VERSION );
-$VERSION = ' $Revision: 1.5 $ ' =~ /\$Revision:\s+([^\s]+)/;
+$VERSION = ' $Revision: 1.6 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 use base 'Slash::DB::Utility';	# first for object init stuff, but really
 				# needs to be second!  figure it out. -- pudge
@@ -76,25 +76,19 @@
 		main::tagboxLog("Despam->feed_newtags called for " . scalar(@$tags_ar) . " tags " . $tags_ar->[0]{tagid} . " ... " . $tags_ar->[-1]{tagid});
 	}
 
-	# The algorithm of the importance of tags to this tagbox is simple
-	# (at least for now).  'binspam' from an admin on a firehose item
-	# is important.  Other tags are not.
 	my $slashdb = getCurrentDB();
 	my $admins = $slashdb->getAdmins();
 
 	my $ret_ar = [ ];
 	for my $tag_hr (@$tags_ar) {
+		# All admin binspam tags are equally important.
+		# All other tags are equally unimportant :)
 		next unless $tag_hr->{tagnameid} == $self->{spamid} && $admins->{ $tag_hr->{uid} };
 		my $ret_hr = {
+			tagid =>	$tag_hr->{tagid},
 			affected_id =>	$tag_hr->{globjid},
 			importance =>	1,
 		};
-		# We identify this little chunk of importance by either
-		# tagid or tdid depending on whether the source data had
-		# the tdid field (which tells us whether feed_newtags was
-		# "really" called via feed_deactivatedtags).
-		if ($tag_hr->{tdid})	{ $ret_hr->{tdid}  = $tag_hr->{tdid}  }
-		else			{ $ret_hr->{tagid} = $tag_hr->{tagid} }
 		push @$ret_ar, $ret_hr;
 	}
 	return [ ] if !@$ret_ar;
@@ -111,16 +105,19 @@
 	my %fh_globjs = ( map { $_, 1 } @$fh_globjs_ar );
 	$ret_ar = [ grep { $fh_globjs{ $_->{affected_id} } } @$ret_ar ];
 
-	main::tagboxLog("Despam->feed_newtags returning " . scalar(@$ret_ar));
+	main::tagboxLog("Despam->feed_newtags returning " . scalar(@$ret_ar) . ": '@$ret_ar'");
 	return $ret_ar;
 }
 
 sub feed_deactivatedtags {
 	my($self, $tags_ar) = @_;
-	main::tagboxLog("Despam->feed_deactivatedtags called: tags_ar='" . join(' ', map { $_->{tagid} } @$tags_ar) .  "'");
-	my $ret_ar = $self->feed_newtags($tags_ar);
-	main::tagboxLog("Despam->feed_deactivatedtags returning " . scalar(@$ret_ar));
-	return $ret_ar;
+	# XXX This need not do anything, I don't think -- not even call
+	# feed_newtags.
+	# The way Despam is set up, 2 admin binspam tags will mark a globjid,
+	# and even if 1 of them is deactivated a moment later, we have no way
+	# to undo the process.
+	main::tagboxLog("Despam->feed_deactivatedtags called: tags_ar='" . join(' ', map { $_->{tagid} } @$tags_ar) .  "', returning nothing");
+	return [ ];
 }
 
 sub feed_userchanges {
@@ -151,34 +148,36 @@
 	my $submitter_uid = $fhitem->{uid};
 	my $submitter_srcid = $fhitem->{srcid_32};
 
-	main::tagboxLog(sprintf("%s->run marking fhid %d (%d) as is_spam", ref($self), $fhid, $affected_id));
+	my $binspam_count = $slashdb->sqlCount(
+		'tags, firehose',
+		"tags.uid IN ($admin_in_str)
+		 AND tags.inactivated IS NULL
+		 AND tags.tagnameid = $self->{spamid}
+		 AND tags.globjid = firehose.globjid
+		 AND firehose.uid = $submitter_uid");
+	my $binspam_tagids = $slashdb->sqlSelectColArrayref(
+		'tagid',
+		'tags',
+		"tags.uid IN ($admin_in_str)
+		 AND tags.inactivated IS NULL
+		 AND tags.tagnameid = $self->{spamid}
+		 AND tags.globjid = firehose.globjid
+		 AND firehose.uid = $submitter_uid");
+	main::tagboxLog(sprintf("%s->run marking fhid %d (%d) as is_spam (for count %d on uid %d: '%s')",
+		ref($self), $fhid, $affected_id, $binspam_count, $submitter_uid, join(' ', @$binspam_tagids)));
 	$firehose_db->setFireHose($fhid, { is_spam => 'yes' });
 
 	if (isAnon($submitter_uid)) {
 		# Non-logged-in user, check by IP (srcid_32)
-		if ($submitter_srcid) {
-			my $binspam_count = $slashdb->sqlCount(
-				'tags, firehose',
-				"tags.uid IN ($admin_in_str)
-				 AND tags.inactivated IS NULL
-				 AND tags.tagnameid = $self->{spamid}
-				 AND tags.globjid = firehose.globjid
-				 AND firehose.srcid_32 = $submitter_srcid");
-			if ($binspam_count > $constants->{tagbox_despam_binspamsallowed_ip}) {
-				main::tagboxLog(sprintf("%s->run marking srcid %s for %d admin binspam tags, based on %d (%d)",
-				ref($self), $submitter_srcid, $binspam_count, $fhid, $affected_id));
-				$self->despam_srcid($submitter_srcid, $binspam_count);
-			}
+		if ($submitter_srcid &&
+			$binspam_count > $constants->{tagbox_despam_binspamsallowed_ip}
+		) {
+			main::tagboxLog(sprintf("%s->run marking srcid %s for %d admin binspam tags, based on %d (%d)",
+			ref($self), $submitter_srcid, $binspam_count, $fhid, $affected_id));
+			$self->despam_srcid($submitter_srcid, $binspam_count);
 		}
 	} else {
 		# Logged-in user, check by uid
-		my $binspam_count = $slashdb->sqlCount(
-			'tags, firehose',
-			"tags.uid IN ($admin_in_str)
-			 AND tags.inactivated IS NULL
-			 AND tags.tagnameid = $self->{spamid}
-			 AND tags.globjid = firehose.globjid
-			 AND firehose.uid = $submitter_uid");
 		if ($binspam_count > $constants->{tagbox_despam_binspamsallowed}) {
 			main::tagboxLog(sprintf("%s->run marking uid %d for %d admin binspam tags, based on %d (%d)",
 				ref($self), $submitter_uid, $binspam_count, $fhid, $affected_id));

Modified: slashjp/branches/upstream/current/tagboxes/FireHoseScores/FireHoseScores.pm
===================================================================
--- slashjp/branches/upstream/current/tagboxes/FireHoseScores/FireHoseScores.pm	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/tagboxes/FireHoseScores/FireHoseScores.pm	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: FireHoseScores.pm,v 1.7 2007/11/13 17:25:04 jamiemccarthy Exp $
+# $Id: FireHoseScores.pm,v 1.8 2008/01/18 21:27:20 jamiemccarthy Exp $
 
 package Slash::Tagbox::FireHoseScores;
 
@@ -28,7 +28,7 @@
 use Data::Dumper;
 
 use vars qw( $VERSION );
-$VERSION = ' $Revision: 1.7 $ ' =~ /\$Revision:\s+([^\s]+)/;
+$VERSION = ' $Revision: 1.8 $ ' =~ /\$Revision:\s+([^\s]+)/;
 
 use base 'Slash::DB::Utility';	# first for object init stuff, but really
 				# needs to be second!  figure it out. -- pudge
@@ -313,11 +313,11 @@
 	my $curhour = $prevhour+3600;
 	my $nexthour = $prevhour+3600;
 	my $tagsdb = getObject('Slash::Tags');
-	$cache->{$prevhour} = $tagsdb->sqlSelect('udc', 'tags_udc', "hourtime=FROM_UNIXTIME($prevhour)")
+	$cache->{$prevhour} = $tagsdb->sqlSelect('udc', 'tags_udc', "hourtime=FROM_UNIXTIME($prevhour)") || 0
 		if !defined $cache->{$prevhour};
-	$cache->{$curhour}  = $tagsdb->sqlSelect('udc', 'tags_udc', "hourtime=FROM_UNIXTIME($curhour)")
+	$cache->{$curhour}  = $tagsdb->sqlSelect('udc', 'tags_udc', "hourtime=FROM_UNIXTIME($curhour)")  || 0
 		if !defined $cache->{$curhour};
-	$cache->{$nexthour} = $tagsdb->sqlSelect('udc', 'tags_udc', "hourtime=FROM_UNIXTIME($nexthour)")
+	$cache->{$nexthour} = $tagsdb->sqlSelect('udc', 'tags_udc', "hourtime=FROM_UNIXTIME($nexthour)") || 0
 		if !defined $cache->{$nexthour};
 	my $prevudc = $cache->{$prevhour};
 	my $curudc  = $cache->{$curhour};
@@ -329,7 +329,8 @@
 	my $udc = $prevudc*$prevweight + $curudc*$curweight + $nextudc*$nextweight;
 	if ($udc == 0) {
 		# This shouldn't happen on a site with any reasonable amount of
-		# up and down voting.  If it does, punt.
+		# up and down voting, unless the tags_udc task has failed to run
+		# for the past hour or two.  If it does happen, punt.
 		main::tagboxLog(sprintf("get_udc_mult punting prev %d %.6f cur %d %.6f next %d %.6f time %d thru %.6f prevw %.6f curw %.6f nextw %.6f",
 			$prevhour, $cache->{$prevhour}, $curhour, $cache->{$curhour}, $nexthour,  $cache->{$nexthour},
 			$time, $thru_frac, $prevweight, $curweight, $nextweight));

Modified: slashjp/branches/upstream/current/themes/slashcode/htdocs/images/comments.js
===================================================================
(Binary files differ)

Modified: slashjp/branches/upstream/current/themes/slashcode/tasks/balance_readers.pl
===================================================================
--- slashjp/branches/upstream/current/themes/slashcode/tasks/balance_readers.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/themes/slashcode/tasks/balance_readers.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: balance_readers.pl,v 1.16 2007/08/29 15:58:58 jamiemccarthy Exp $
+# $Id: balance_readers.pl,v 1.17 2008/01/16 17:28:14 jamiemccarthy Exp $
 
 # For now this just gathers data.  The actual reweighting will come
 # later. - Jamie 2004/11/10
@@ -379,11 +379,11 @@
 
 		# If this DB is marked as isalive='no', it's not our
 		# responsibility, skip it.
-		next VU if $reader_info->{dead};
+		next VU if $reader_info->{$vu}{dead};
 
 		# If this DB was not reachable, set its weight to 0
 		# immediately.
-		if ($reader_info->{unreachable}) {
+		if ($reader_info->{$vu}{unreachable}) {
 			set_reader_weight_adjust($slashdb, $vu, 0);
 			next VU;
 		}

Modified: slashjp/branches/upstream/current/themes/slashcode/tasks/ircslash.pl
===================================================================
--- slashjp/branches/upstream/current/themes/slashcode/tasks/ircslash.pl	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/themes/slashcode/tasks/ircslash.pl	2008-01-22 10:59:02 UTC (rev 449)
@@ -2,7 +2,7 @@
 # This code is a part of Slash, and is released under the GPL.
 # Copyright 1997-2005 by Open Source Technology Group. See README
 # and COPYING for more information, or see http://slashcode.com/.
-# $Id: ircslash.pl,v 1.46 2008/01/09 21:00:06 pudge Exp $
+# $Id: ircslash.pl,v 1.48 2008/01/18 22:36:51 pudge Exp $
 
 use strict;
 
@@ -254,13 +254,13 @@
 	# we want to msg ourselves to catch the current time from the
 	# Jabber server, so we can skip messages from the channel log
 	# when we enter the channel
-	my $to = "$jchannel\@$jchanserver/$jnick";
-	slashdLog("sending to: $to");
-	$jabber->MessageSend(
-		to		=> $to,
-		type		=> 'chat',
-		body		=> 'timestamp',
-	);
+# 	my $to = "$jchannel\@$jchanserver/$jnick";
+# 	slashdLog("sending to: $to");
+# 	$jabber->MessageSend(
+# 		to		=> $to,
+# 		type		=> 'chat',
+# 		body		=> 'timestamp',
+# 	);
 
 	# XXX: this will silently fail on a nick collision ...
 	$jabber->MUCJoin(
@@ -861,8 +861,8 @@
 			$ok = 0 if !$dbs_data->{$dbid}{was_alive}
 				|| !$dbs_data->{$dbid}{was_reachable}
 				|| !$dbs_data->{$dbid}{was_running};
-			$ok = 0 if $dbs_data->{$dbid}{lag} > ($constants->{ircslash_dbalert_lagthresh} || 30);
-			$ok = 0 if $dbs_data->{$dbid}{bog} > ($constants->{ircslash_dbalert_bogthresh} || 30);
+			$ok = 0 if $ok && $dbs_data->{$dbid}{lag} > ($constants->{ircslash_dbalert_lagthresh} || 30);
+			$ok = 0 if $ok && $dbs_data->{$dbid}{bog} > ($constants->{ircslash_dbalert_bogthresh} || 30);
 		}
 		# "Great" means good enough to clear out a previously
 		# reported alert.
@@ -871,8 +871,8 @@
 			$great = 0 if !$dbs_data->{$dbid}{was_alive}
 				|| !$dbs_data->{$dbid}{was_reachable}
 				|| !$dbs_data->{$dbid}{was_running};
-			$great = 0 if $dbs_data->{$dbid}{lag} > ($constants->{ircslash_dbalert_lagthresh} || 30)/2;
-			$great = 0 if $dbs_data->{$dbid}{bog} > ($constants->{ircslash_dbalert_bogthresh} || 30)/2;
+			$great = 0 if $great && $dbs_data->{$dbid}{lag} > ($constants->{ircslash_dbalert_lagthresh} || 30)/2;
+			$great = 0 if $great && $dbs_data->{$dbid}{bog} > ($constants->{ircslash_dbalert_bogthresh} || 30)/2;
 		}
 		if (!$ok) {
 			# There's something about the DBs that we

Modified: slashjp/branches/upstream/current/themes/slashcode/templates/printCommComments;misc;default
===================================================================
--- slashjp/branches/upstream/current/themes/slashcode/templates/printCommComments;misc;default	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/themes/slashcode/templates/printCommComments;misc;default	2008-01-22 10:59:02 UTC (rev 449)
@@ -42,7 +42,7 @@
 <!--
 	window.onscroll = d2act;
 	boxStatus(1);
-[% Slash.jsSelectComments(Slash.db, constants, user, { sid => sid, cid => cid }) %]
+[% Slash.jsSelectComments(Slash.db, constants, user, { sid => sid, cid => cid }, gSkin) %]
 //-->
 	</script>
 [% END %]
@@ -155,4 +155,4 @@
 __seclev__
 10000
 __version__
-$Id: printCommComments;misc;default,v 1.63 2007/10/24 00:30:08 scc Exp $
+$Id: printCommComments;misc;default,v 1.64 2008/01/18 22:36:51 pudge Exp $

Modified: slashjp/branches/upstream/current/themes/slashcode/templates/printCommentsMain;misc;default
===================================================================
--- slashjp/branches/upstream/current/themes/slashcode/templates/printCommentsMain;misc;default	2008-01-22 08:47:26 UTC (rev 448)
+++ slashjp/branches/upstream/current/themes/slashcode/templates/printCommentsMain;misc;default	2008-01-22 10:59:02 UTC (rev 449)
@@ -115,6 +115,7 @@
 	<div class="commentBoxForm" id="commentControlBox">
 [% UNLESS discussion2 %]
 		<form action="[% gSkin.rootdir %]/comments.pl">
+		[% IF user.state.no_d2 %]<input type="hidden" name="no_d2" value="1">[% END %]
 		<fieldset>
 		<legend>Display Options</legend>
 		[% UNLESS discussion2 %]
@@ -201,7 +202,7 @@
 			}) %]
 		[% END %]
 		</span>
-		<span title="Toggle window location" onclick="toggleDisplayOptions()" class="close">/</span>
+		<span id="d2toggle" title="Toggle window location" onclick="toggleDisplayOptions()" class="close">/</span>
 				</h4>
 			</div>
 		</div>
@@ -304,4 +305,4 @@
 __seclev__
 10000
 __version__
-$Id: printCommentsMain;misc;default,v 1.99 2008/01/07 16:25:17 scc Exp $
+$Id: printCommentsMain;misc;default,v 1.101 2008/01/21 16:49:48 scc Exp $


Slashdotjp-dev メーリングリストの案内
Back to archive index