openid_verify($cgi,$forum);
} elsif ($cgi->param("logout")) {
logout('logout',$cgi,$forum);
+ } elsif ($cgi->param("profile")) {
+ show_profile("profile",$cgi,$forum);
} else {
for my $param ($cgi->param) {
# Среди параметров, указанных в URL ищем тот, который задает
sub show_error {
my ($cfg,$msg) = @_;
if ( -r $cfg->{"templates"}."/error.html") {
- my $tree = HTML::TreeBuilder->new_from_file($cfg->{"templates"}."/error.html");
+ my $tree = treefromfile($cfg->{"templates"}."/error.html");
my $node= $tree->find_by_attribute('class','error');
my $body;
if (!$node) {
$node->delete_content;
$node->push_content($msg);
print $cgi->header(-type=>'text/html',-charset=>'utf-8');
- print $tree->as_HTML("<>&");
+ print output_html($tree);
} else {
print $cgi->header(-type=>'text/html',-charset=>'utf-8');
print "<html><head><title>Ошибка конфигурации форума</title></head>",
# подставляются
#
sub show_template {
+ my $tree = prepare_template(@_);
+ send_to_user($tree,@_);
+ exit;
+}
+sub send_to_user {
+ my ($tree,$form,$cgi,$forum) = @_;
+ print
+ $cgi->header(-type=>"text/html",-charset=>"utf-8",($forum->{cookies}?(-cookie=>$forum->{cookies}):())),
+ output_html($tree);
+}
+sub prepare_template {
my ($form,$cgi,$forum) = @_;
my $tree = gettemplate($forum,$form,$ENV{'PATH_INFO'});
ELEMENT:
for my $element ($f->find_by_tag_name("textarea","input","select")) {
my $name = $element->attr("name");
+ #print STDERR "Found element <".$element->tag()." name=\"$name\">\n" ;
+ #print STDERR "Corresponding \$cgi->param($name)=\"",$cgi->param($name),"\"\n";
$substituted{$name} = 1;
- #print STDERR "substituting form element name $name tag ",$element->tag,
- # "value='",$cgi->param($name),"'\n";
if (defined $cgi->param($name)) {
if ($element->tag eq "input") {
- next ELEMENT if grep ($element->attr("type") eq
- $_,"button","submit","reset");
- if ($element->attr("type") eq "check") {
+ my $type=$element->attr('type') || "text";
+ next ELEMENT if grep($type eq $_,
+ "button","submit","reset");
+ if ($type eq "check") {
if (grep($element->attr("value") eq $_,$cgi->param($name))) {
$element->attr("checked","");
} else {
$element->attr("checked",undef);
}
- } elsif ($element->attr("type") eq
+ } elsif ($type eq
"radio") {
if ($element->attr("value") eq $cgi->param($name)) {
$element->attr("checked","");
$element->push_content($cgi->param($name));
} elsif ($element->tag eq "select") {
for my $option ($element->find_by_tag_name("option")) {
- if (grep($option->attr("value") eq $_, $cgi->param($name))) {
+ my $value = $option->attr("value") ||
+ $option->as_text();
+ if (grep($value eq $_, $cgi->param($name))) {
$option->attr("selected","");
} else {
$option->attr("selected",undef);
$f->push_content($element);
}
}
-
-
- print
- $cgi->header(-type=>"text/html",-charset=>"utf-8",($forum->{cookies}?(-cookie=>$forum->{cookies}):())),
- $tree->as_HTML("<>&");
- exit;
-}
+ return $tree;
+}
#
# Поправляет ссылки на служебные файлы и скрипты форума
#
}
# Обрабатываем наши специальные link rel=""
+ my $userlist = $cgi->url(-absolute=>1,
+ -path_info=>0,-query_string=>0).$forum->{userurl};
if ($element->tag eq "link") {
if ($element->attr("rel") eq "forum-user-list") {
- $element->attr("href" => $cgi->url(-absolute=>1,
- -path_info=>0,-query_string=>0).$forum->{userurl});
+ $element->attr("href" => $userlist);
next ELEMENT;
} elsif ($element->attr("rel") eq "forum-script") {
$element->attr("href" => $script_with_path);
eq"."||$link eq "..");
# Ссылка от корня сайта.
if (substr($link,0,1) eq "/") {
+ # Если там два слэша, заменяем их на forumtop
+ if (substr($link,0,2) eq '//') {
+ $element->attr($attr, $forum->{forumtop}.substr($link,1));
+ next ELEMENT;
+ }
# Если она не ведет на наш скрипт, не обрабатываем
next ELEMENT if substr($link,0,length($ENV{SCRIPT_NAME}) ne
$ENV{SCRIPT_NAME}) ;
$link =~ s/^[^\?]+/forum/;
}
if (!($link =~ s!^templates/!$forum->{templatesurl}/!) &&
- !($link =~ s!^users/!$forum->{usersurl}/!) &&
+ !($link =~ s!^users/!$userlist/!) &&
!($link =~ s!^forum\b!$script_with_path!)) {
$link = $forum->{"forumtop"}."/".$link
}
my %userbase;
dbmopen %userbase,datafile($forum,"passwd"),0644;
if ( $userbase{$user}) {
- print STDERR "getting user info for $user\n";
my $userinfo = thaw($userbase{$user});
delete $userinfo->{"passwd"};
$userinfo->{"user"} = $user;
exit;
}
#
+# Заполнение формы редактирования профиля данными пользователя
+
+sub show_profile {
+ my ($formname,$cgi,$forum) = @_;
+ my $rights = getrights($cgi,$forum);
+ my $user = $cgi->param("user");
+ if (!$user && substr($path_translated,length($forum->{userdir}) eq
+ $forum->{userdir})) {
+ $user = substr($path_translated,length($forum->{userdir})+1);
+ }
+ $user = $forum->{authenticated}{user} unless $user;
+ show_error($forum,"Чей профиль вы хотите редактировать?")
+ unless $user;
+ my %base;
+ dbmopen %base,datafile($forum,"passwd"),0664;
+ show_error($forum,"Нет такого пользователя $user")
+ unless $base{$user};
+ my $userinfo = thaw($base{$user});
+ dbmclose(%base);
+ delete $userinfo->{passwd};
+ $userinfo->{user}=$user;
+ print STDERR "Substituting userinfo for $user\n";
+ while(my ($field,$value) = each %$userinfo) {
+ $value = $value->{src} if ($field eq 'avatar' && ref($value));
+ $cgi->param($field,$value);
+ }
+ my $tree = prepare_template(@_);
+ # Запрещаем редактирование полей, входящих в restricted_user_info
+ my $form = $tree->look_down(_tag=>"form",name=>"profile");
+ if ($rights ne "admin" && $forum->{restricted_user_info}) {
+ for my $field (split /\s*,\s*/,$forum->{restricted_user_info}) {
+ ELEMENT:
+ for my $element ($form->look_down(name=>$field)) {
+ my $tag= $element->tag;
+ if ($tag eq 'input') {
+ my $newel=new HTML::Element("span",
+ "class"=>"restricted-field");
+
+ $newel->push_content($element->attr("value"));
+ $element->replace_with($newel)->delete();
+ } elsif ($tag eq 'textarea') {
+ $element->replace_with_content(new HTML::Element("div",
+ class=>"restricted-field"))->delete();
+ } elsif ($tag eq 'select') {
+ my $newel = new HTML::Element("span",
+ class=>"restricted-field");
+ OPTION:
+ for my $option ($element->content_list) {
+ if (ref $option eq "HTML::Element" &&
+ $option->attr("selected")) {
+ $newel->push_content($option->detach_content());
+ last OPTION;
+ }
+ }
+ if (!$newel->content_list) {
+ $newel->push_content(($element->content_list)[0]);
+ }
+ $element->replace_with($newel)->delete;
+ }
+ }
+ }
+ }
+ # Подставляем аватарку
+ print STDERR "avatar=",$userinfo->{avatar},"\n";
+ substinfo($tree,[_tag=>'img',class=>'avatar'],(ref($userinfo->{avatar})?(%{$userinfo->{avatar}}):(src=>$userinfo->{avatar})));
+ for my $userlink ($tree->look_down(_tag => "a",class=>"author")) {
+ $userlink->delete_content;
+ $userlink->push_content($user);
+ if ($forum->{authenticated}{openiduser}) {
+ $userlink->attr('href'=>"http://$user");
+ } else {
+ $userlink->attr('href'=>undef);
+ $userlink->tag('span');
+ }
+ }
+ send_to_user($tree,@_);
+}
+# Обработка результатов редактирования профиля пользвателя
+#
+sub profile {
+ my ($formname,$cgi,$forum) = @_;
+
+}
+#
# Обработка результатов заполнения формы регистрации.
#
#
}
}
}
- my $page =
- $tree->as_HTML("<>&");
+ my $page = output_html($tree);
my $length = do {use bytes; length($page);};
print $cgi->header(-type=>"text/html",-content_length=>$length,
-charset=>"utf-8",($forum->{cookies}?(-cookie=>$forum->{cookies}):())),
foreach my $profile_link ($tree->look_down(_tag=>"a",
href=>qr/profile=/)) {
if ((defined $rights && $rights eq "admin")||
- (defined $forum->{autheticated}{user} &&
+ (defined $forum->{authenticated}{user} &&
$forum->{authenticated}{user} eq $user)) {
$profile_link->attr("href",
#
# Генерируем идентификатор записи.
#
- my $id = get_uid($forum);
+ my $id = "m".get_uid($forum);
#
show_error($forum,"В форме управления сообщением нет поля author");
}
# Подставляем mdate
+ my $posted = strftime("%d.%m.%Y %H:%M",localtime());
substinfo($newmsg,["class"=>"mdate"],
- _content =>strftime("%d.%m.%Y %H:%M",localtime()));
+ _content =>$posted);
# Подставляем mreply
substinfo($newmsg,[_tag=>"a","class"=>"mreply"],"href" =>
$cgi->url(-absolute=>1,-path_info=>1)."?reply=1&id=$id");
substinfo($newmsg,[_tag => "a",class=>"mparent"],
style=>"display: none;");
}
-
+ my $msgcount=0;
+ for my $msg ($newmsg->parent->look_down("class"=>"message")) {
+ $msgcount ++;
+ }
+
#
# Делаем Уфф и сохраняем то, что получилось
#
+ record_as_recent($forum,$newmsg->clone);
savetree($path_translated,$tree,$lockfd);
record_statistics($forum,"message"),
+ update_topic_list($forum,$path_translated,$msgcount,$posted);
forum_redirect($cgi,$forum);
}
+sub update_topic_list {
+ my ($forum,$topic,$count,$date) = @_;
+ my ($tree,$lockfd,$block,$index);
+ if (!ref ($topic)) {
+ # Если $topic - имя файла, найдем соответствующий индекс и в нем
+ # элемент с соответствующим id;
+ my ($dir,$id)=($1,$2) if $topic =~/(.+)\/([^\/]+).html/;
+ $index = $dir."/".$forum->{indexfile};
+ ($tree,$lockfd) = gettree($index);
+ $block = $tree->look_down("id"=>$id);
+ return unless $block;
+ } else {
+ # Иначе нам передали кусок готового распарсенного дерева
+ $block = $topic;
+ }
+ substinfo($block,[class=>"msgcount"],_content=>$count);
+ substinfo($block,[class=>"last-updated"],_content=>$date);
+ # и если мы парсили дерево, то мы его и сохраняем
+ savetree($index,$tree,$lockfd);
+}
+
+sub record_as_recent {
+ my ($forum,$msg) = @_;
+ my ($tree,$lockfd) = gettree($forum->{forumroot}."/recent.html");
+ my $msglist = $tree->look_down("class"=>"messagelist");
+ if ($msglist) {
+ my $style = $msglist->attr("style");
+ if ($style && $style =~ s/display: none;//) {
+ $msglist->attr("style",$style);
+ $msglist->look_down(class=>"message")->replace_with($msg);
+ } else {
+ my $prev = $msglist->look_down("class"=>"message");
+ $prev->preinsert($msg);
+ }
+ }
+ savetree($forum->{forumroot}."/recent.html",$tree,$lockfd);
+}
#
# Обработка операции создания новой темы.
#
$newtopic->attr("id",$urlname);
my $controlform = $newtopic->look_down(_tag=>"form",class=>"topicinfo");
if ($controlform) {
- $controlform->attr("action"=>$url);
+ $controlform->attr("action"=>$cgi->url(-absolute=>1,-path_info=>0,
+ -query_string=>0).$url);
substinfo($controlform,[_tag=>"input",name=>"author"],value=>
$forum->{authenticated}{user});
}
+ update_topic_list($forum,$newtopic,0,$creation_time);
savetree($path_translated."/".$forum->{"indexfile"},$tree,$lockfd);
record_statistics($forum,"topic");
forum_redirect($cgi,$forum,$cgi->url(-base=>1).$url);
#
my $url = $cgi->path_info."/$urlname";
+ $url= $cgi->path_info if $urlname eq ".";
$url =~ s/\/+/\//g;
my $tree = gettemplate($forum,"forum",$url);
+ # Удалить элементы, который присутствуют только на главной странице
+ if ($urlname ne ".") {
+ for my $element ($tree->look_down("class"=>"top-page")) {
+ $element->delete;
+ }
+ }
# Заполнить название и аннотацию
my $abstract = input2tree($cgi,$forum,"abstract");
substinfo($tree,[_tag=>"meta","name"=>"description"],content=>$abstract->as_trimmed_text);
$url);
substinfo($controlform,[_tag=>"input",name=>"author"],value=>
$forum->{authenticated}{user});
- }
+ }
savetree($path_translated."/".$forum->{"indexfile"},$tree,$lockfd);
record_statistics($forum,"forum");
+ } else {
+ # Создаем тему для "свежих реплик"
+ my $recent = gettemplate($forum,"topic",$url."/recent.html");
+ # remove reply link from page itself
+ for my $link ($recent->look_down(_tag =>"a", href=>qr/reply=/)) {
+ $link->delete;
+ }
+ substinfo($recent,["_tag"=>"title"],$cgi->param("title").": Свежие сообщения");
+ substinfo($recent,["class"=>"title"],
+ _content=>$cgi->param("title"). ": Свежие сообщения");
+ hide_list($recent,"messagelist");
+ savetree($path_translated."/recent.html",$recent,undef);
+
}
+
forum_redirect($cgi,$forum,$cgi->url(-base=>1).$url);
}
my $user_status = "normal";
LEVEL:
while (length($dir)) {
- print STDERR "Searcghing for perms in $dir\n";
if (-f "$dir/perms.txt") {
open $f,"<","$dir/perms.txt";
my $status = undef;
my $f;
open $f,"<",$filename or return undef;
flock $f, LOCK_EX;
- my $tree = HTML::TreeBuilder->new_from_file($f);
+ my $tree = treefromfile($f);
+ $tree->parse_file($f);
return ($tree,$f);
}
#
my ($filename,$tree,$lockfd) = @_;
my $f;
open $f,">",$filename . ".new" or return undef;
- print $f $tree->as_HTML("<>&");
+ print $f output_html($tree);
close $f;
# FIXME - только для POSIX.
unlink $filename;
close $lockfd if defined($lockfd);
}
#
+# Cериализовать HTML-документ с DOCTYPE (workaround вокруг баги в
+# HTML::TreeBuilder)
+#
+sub output_html {
+ my $tree=shift;
+ return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'.
+ $tree->as_HTML("<>&");
+}
+#
# Читает шаблон и подготавливает его к размещению по указанной URL.
# Если url не указана, считается что шаблон будет показан как результат
# текущего http-запроса.
show_error($forum,"Нет шаблона $template");
exit;
}
- my $tree = HTML::TreeBuilder->new_from_file($filename);
+ my $tree = treefromfile($filename);
fix_forum_links($forum,$tree,$url);
return $tree;
}
+#
+# Создает объект HTML::TreeBuilder и выставляет ряд опций.
+#
+sub make_tree {
+ my $tree = HTML::TreeBuilder->new;
+ # Set some options for treebuilder
+ # Comments are neccessary to convert HTML back to BBCode
+ $tree->store_comments(1);
+ # Avoid converting html into one long-long string
+ $tree->ignore_ignorable_whitespace(0);
+ $tree->no_space_compacting(1);
+ $tree->p_strict(1);
+ return $tree;
+}
+
+sub treefromfile {
+ my ($f) = shift;
+ my $tree = make_tree();
+ $tree->parse_file($f);
+ return $tree;
+}
#
# Получает уникальный числовой идентификатор.
#
sub str2tree {
my ($data)=@_;
- my $tree = HTML::TreeBuilder->new();
+ my $tree = make_tree();
# Set parser options here
$tree->parse("<html><body><div>$data</div></body></html>");
$tree->eof;