]> wagner.pp.ru Git - oss/stilllife.git/commitdiff
*** empty log message ***
authorVictor Wagner <vitus@wagner.pp.ru>
Mon, 31 Mar 2008 18:35:57 +0000 (18:35 +0000)
committerVictor Wagner <vitus@wagner.pp.ru>
Mon, 31 Mar 2008 18:35:57 +0000 (18:35 +0000)
doc/profile.txt
doc/templates.txt
forum/TODO
forum/forum

index 967be3b7c939bc5af1000f1a6954dfa376a56f56..2d59894bba54540f3a2017aaa83b8f74ab3311bd 100644 (file)
@@ -87,3 +87,16 @@ restricted_user_info, недоступны для редактирования 
 Для этого следует не использовать в элементах option атрибут value. При
 отсутствии этого атрибута будет использовано значение текста пункта меню.
 
+
+Обязательные поля
+
+Если в форме регистрации присутсвует скрытое поле с названием required,
+и его значение представляет собой перечисленный через запятуз список
+полей, то незаполнение этих полей будут проинтерпретированы как ошибка.
+
+Поля форм регистрации и редактирования профиля, которые не являются
+атрибутами пользователя.
+
+Если в форме присутствует скрытое поле ignore, значение которого
+представляет собой список полей формы через запятую, значения этих полей
+будут проигнорированы при создании профайла пользователя.
index fde12d50fecdab6c614f88e956b9c1c1bb225cc8..a9952445328a2f14fbdd5036778fd9ac12d5d4a7 100644 (file)
@@ -45,9 +45,15 @@ author - ник автора сообщения. innerHtml заменяется
 mdate  - дата публикации сообщения  innerHtml заменяется на дату
 avatar - элемент img атрибут src  которого заменияется на аватар автора,
        или на templates/1x1.gif если у автора нет аватара.
-astatus - статус автора на форуме innerHtml заменяется на статус
-acomment - комментарий к нику, введенный автором при регистрации.
+ap-status - статус автора на форуме innerHtml заменяется на статус
+ap-comment - комментарий к нику, введенный автором при регистрации.
          innerHtml заменяется на комментарий
+И прочие классы с префиксом ap-, innerHtml которых заменяеняется 
+на соответствущие поля из профайла автора. Если поле имеет в имени 
+подчерк, допустимо вместо подчерка использовать дефис "-" в названии
+класа.  
+
+
 msubject - тема сообщения. Заменяется innerHtml          
 mtext - текст сообщения. innerHtml заменяется на отформатированный текст 
 mreply - ссылка на скрипт ответа. Атрибут href будет заменен на
@@ -146,18 +152,29 @@ mreply - ссылка на скрипт ответа. Атрибут href буд
 
 Страница списка тем (головная страница форума)
 
-Может иметь элемент с классом header, описывающий форум в целом (его
-создатель, вводный текст и т.д.
+Может иметь элемент с классом annotation, описывающий форум в целом (его
+создатель, вводный текст и т.д. устроенный внутри аналогично элементу
+списка форумов (см ниже). Если в шаблоне присутствует элемент meta
+name="description", то туда помещается текстовое представление аннотации
+форума.
+
+Если в шаблоне присутствуют элементы с классом top-page, то они будут
+сохранены только на головной странице форума, а при создании подфорумов
+будут из их оглавлений удаляться.
 
 Шаблоном описания конкретной темы является элемент с классом topic,
-устроенный аналогично message (показывается текст и автор первой реплики
-темы) с той разницей, что элемент с классом subject должен быть ссылкой.
+Содержащий элемент с классом title (название темы, должно быть
+ссылкой), abstract (аннотация темы)
+author (ссылка), date (дата создания темы), tlink (якорь для ссылок на
+элемент списка тем ), last-updated и msgcount.  
 
 Внутри элемента с классом topic должна присутствовать форма
 с кнопками edit delete move setrights и скрытым полем  id. 
 Кнопку setrights следует показывать только пользователю с правами
 администратора.
 
+
+
 элемент с классом topic должен быть заключен в элемент с классом
 topiclist.
 
@@ -168,8 +185,9 @@ topiclist.
 вставляется непосредственно за предыдущим
 
 
-Кроме этого, cтраница должна иметь ссылку с классом newtopic или форму с именем 
-newtopic и кнопкой submit с именем newtopic
+Кроме этого, cтраница должна иметь ссылку на форумный скрипт с
+параметром newtopic=1 или форму с именем 
+topicinfo и кнопкой submit с именем newtopic
 
 Список подфорумов устроен аналогично списку тем.
 
index d4990843c54b55065fe1f3d0787f4382bcd76a37..d107b2fb3adc1334db78fe542b60a8f035c180c7 100644 (file)
@@ -1,12 +1,20 @@
 Roadmap по server-side части
-1. процедура раскрутки
-2. страничка юзера
-3. список юзеров
-4. редактирование user profile и фиксы в регистрации
-5. Механизм регистрации с подтверждением
-6. delete (message, topic, forum)
-7. edit (message,topic,forum)
-8. move (message,topic,forum)
-9. setrights
-10. applytemplates 
-11. Почтовые оповещения о новых репликах, RSS или recent comments page
+
++ 1. Генерация валидного HTML 4.01 
++ 2. страничка юзера
++ 3. список юзеров (отпрофайлить)
++ 4. Показ формы редактирования профиля 
+5      редактирование user profile 
+6. Статистика по репликам на странице форума
+7  recent comments page
+8. Придумать способ прописывать в шаблонах ссылки на страницы  
+8. delete (message, topic, forum)
+9. процедура раскрутки
+10. Механизм регистрации с подтверждением
+11. edit (message,topic,forum)
+12. move (message,topic,forum)
+13. setrights
+14. applytemplates 
+15. Почтовые оповещения о новых репликах, RSS или 
+16. Блок top_page_only в шаблоне форума. 
+
index 9f12ad841445c91abe6aa7519661e8fef9b86df5..d2ae68623d1e6def113126e65dd7a280feba3108 100755 (executable)
@@ -73,6 +73,8 @@ if ($cgi->request_method ne "POST") {
                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 ищем тот, который задает
@@ -243,7 +245,7 @@ sub get_forum_config {
 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) {
@@ -254,7 +256,7 @@ sub show_error {
                $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>",
@@ -272,6 +274,17 @@ sub show_error {
 # подставляются
 #
 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'});
 
@@ -314,21 +327,22 @@ sub show_template {
        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","");
@@ -343,7 +357,9 @@ sub show_template {
                                $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);
@@ -363,13 +379,8 @@ sub show_template {
                        $f->push_content($element);
                }
        }       
-                               
-               
-       print
-       $cgi->header(-type=>"text/html",-charset=>"utf-8",($forum->{cookies}?(-cookie=>$forum->{cookies}):())),
-       $tree->as_HTML("<>&");
-       exit;
-}
+       return $tree;
+}      
 #
 # Поправляет ссылки на служебные файлы и скрипты форума
 #
@@ -392,10 +403,11 @@ sub fix_forum_links {
                }
                
                # Обрабатываем наши специальные 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);
@@ -408,6 +420,11 @@ sub fix_forum_links {
                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}) ;
@@ -416,7 +433,7 @@ sub fix_forum_links {
                        $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 
                }       
@@ -515,7 +532,6 @@ sub authorize_user  {
                                        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;
@@ -654,6 +670,90 @@ sub forum_redirect {
        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) = @_; 
+
+}
+#
 # Обработка результатов заполнения формы регистрации.
 #
 #
@@ -813,8 +913,7 @@ sub show_user_page {
                        }       
                }
        }       
-       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}):())),
@@ -825,7 +924,7 @@ sub profile_links {
        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",
@@ -911,7 +1010,7 @@ sub reply {
        #       
        # Генерируем идентификатор записи.
        #
-       my $id = get_uid($forum);
+       my $id = "m".get_uid($forum);
 
 
        #
@@ -984,8 +1083,9 @@ sub reply {
                        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");
@@ -1005,15 +1105,58 @@ sub reply {
                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);
+}      
 #
 # Обработка операции создания новой темы. 
 #
@@ -1087,10 +1230,12 @@ sub new_topic {
        $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);
@@ -1159,8 +1304,15 @@ sub new_forum {
        #
        
        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);
@@ -1209,10 +1361,24 @@ sub new_forum {
                $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);
 }
        
@@ -1252,7 +1418,6 @@ sub getrights {
        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;
@@ -1297,7 +1462,8 @@ sub gettree {
        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);
 }      
 #
@@ -1309,7 +1475,7 @@ sub savetree {
        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;
@@ -1317,6 +1483,15 @@ sub savetree {
        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-запроса.
@@ -1329,12 +1504,33 @@ sub gettemplate {
                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;
+}      
 #
 # Получает уникальный числовой идентификатор.
 # 
@@ -1521,7 +1717,7 @@ sub input2tree {
 
 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;