1 |
8 |
ahitrov@rambler.ru |
package Contenido::Section; |
2 |
|
|
|
3 |
|
|
# ---------------------------------------------------------------------------- |
4 |
|
|
# Класс Секция. |
5 |
|
|
# Теперь это опять же - базовый класс, вводим в него дополнительную |
6 |
|
|
# функциональность. |
7 |
|
|
# ---------------------------------------------------------------------------- |
8 |
|
|
|
9 |
|
|
use strict; |
10 |
|
|
use warnings; |
11 |
|
|
use locale; |
12 |
|
|
|
13 |
|
|
use vars qw($VERSION $ROOT); |
14 |
|
|
$VERSION = '6.0'; |
15 |
|
|
|
16 |
|
|
use base 'Contenido::Object'; |
17 |
|
|
use Contenido::Globals; |
18 |
|
|
|
19 |
|
|
$ROOT = 1; # Корневая секция |
20 |
|
|
|
21 |
|
|
sub class_name { |
22 |
|
|
return 'Секция'; |
23 |
|
|
} |
24 |
|
|
|
25 |
|
|
sub class_description { |
26 |
|
|
return 'Секция по умолчанию'; |
27 |
|
|
} |
28 |
|
|
|
29 |
|
|
# DEFAULT клас реализации таблицы |
30 |
|
|
sub class_table { |
31 |
|
|
return 'SQL::SectionTable'; |
32 |
|
|
} |
33 |
|
|
|
34 |
|
|
# ---------------------------------------------------------------------------- |
35 |
|
|
# Конструктор. Создает новый объект сессии. |
36 |
|
|
# |
37 |
|
|
# Формат использования: |
38 |
|
|
# Contenido::Section->new() |
39 |
|
|
# Contenido::Section->new($keeper) |
40 |
|
|
# Contenido::Section->new($keeper,$id) |
41 |
|
|
# Contenido::Section->new($keeper,$id,$pid) |
42 |
|
|
# ---------------------------------------------------------------------------- |
43 |
|
|
sub new { |
44 |
|
|
my ($proto, $keeper, $id, $pid) = @_; |
45 |
|
|
my $class = ref($proto) || $proto; |
46 |
|
|
my $self; |
47 |
|
|
|
48 |
|
|
if (defined($id) && ($id>0) && defined($keeper)) { |
49 |
|
|
$self=$keeper->get_section_by_id($id, class=>$class); |
50 |
|
|
} else { |
51 |
|
|
$self = {}; |
52 |
|
|
bless($self, $class); |
53 |
|
|
$self->init(); |
54 |
|
|
$self->keeper($keeper) if (defined($keeper)); |
55 |
|
|
$self->{class} = $class; |
56 |
|
|
$self->id($id) if (defined($id) && ($id > 0)); |
57 |
|
|
$self->pid($pid) if (defined($pid) && ($pid > 0)); |
58 |
|
|
} |
59 |
|
|
|
60 |
|
|
return $self; |
61 |
|
|
} |
62 |
|
|
|
63 |
|
|
sub _get_table { |
64 |
|
|
class_table()->new(); |
65 |
|
|
} |
66 |
|
|
|
67 |
607 |
ahitrov |
sub pre_store { |
68 |
|
|
my $self = shift; |
69 |
|
|
|
70 |
|
|
if ( $self->id && !$self->{__light} ) { |
71 |
|
|
### Autofill or autoflush documents order if applicable |
72 |
|
|
if ( $self->_sorted && !$self->_sorted_order ) { |
73 |
|
|
my %opts; |
74 |
|
|
if ( $self->default_document_class ) { |
75 |
|
|
$opts{class} = $self->default_document_class; |
76 |
|
|
} elsif ( $self->default_table_class ) { |
77 |
|
|
$opts{table} = $self->default_table_class; |
78 |
|
|
} else { |
79 |
|
|
$opts{table} = 'Contenido::SQL::DocumentTable'; |
80 |
|
|
} |
81 |
|
|
if ( $self->order_by ) { |
82 |
|
|
$opts{order_by} = $self->order_by; |
83 |
|
|
} |
84 |
|
|
if ( $self->filters ) { |
85 |
|
|
no strict 'vars'; |
86 |
|
|
my $filters = eval($self->filters); |
87 |
|
|
if ($@) { |
88 |
|
|
warn "Bad filter: " . $self->filters . " in section " . $self->id; |
89 |
|
|
} elsif (ref $filters eq 'HASH') { |
90 |
|
|
while ( my ($key, $val) = each %$filters ) { |
91 |
|
|
$opts{$key} = $val; |
92 |
|
|
} |
93 |
|
|
} |
94 |
|
|
} |
95 |
|
|
my $ids = $keeper->get_documents( s => $self->id, %opts, ids => 1, return_mode => 'array_ref' ); |
96 |
|
|
$self->_sorted_order( join(',', @$ids) ); |
97 |
|
|
} elsif ( !$self->_sorted && $self->_sorted_order ) { |
98 |
|
|
$self->_sorted_order( undef ); |
99 |
|
|
} |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
1; |
103 |
|
|
} |
104 |
|
|
|
105 |
8 |
ahitrov@rambler.ru |
#доработка метода store |
106 |
|
|
sub store { |
107 |
|
|
my $self=shift; |
108 |
|
|
|
109 |
|
|
#для новосозданных секций ставим новый sorder |
110 |
|
|
unless ($self->{id}) { |
111 |
|
|
my ($sorder) = $self->keeper->SQL->selectrow_array("select max(sorder) from ".$self->class_table->db_table(), {}); |
112 |
|
|
$self->{sorder} = $sorder + 1; |
113 |
|
|
} |
114 |
|
|
|
115 |
|
|
return $self->SUPER::store(); |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
|
119 |
|
|
## Специальные свойства для секций с встроенной сортировкой документов |
120 |
|
|
sub add_properties { |
121 |
|
|
return ( |
122 |
|
|
{ # Признак "секция с сортировкой" |
123 |
|
|
'attr' => '_sorted', |
124 |
|
|
'type' => 'checkbox', |
125 |
|
|
'rusname' => 'Ручная сортировка документов', |
126 |
|
|
}, |
127 |
|
|
{ # Порядок документов (список id) |
128 |
|
|
'attr' => '_sorted_order', |
129 |
|
|
'type' => 'string', |
130 |
|
|
'rusname' => 'Порядок документов в секции', |
131 |
|
|
'hidden' => 1 |
132 |
|
|
}, |
133 |
|
|
{ |
134 |
|
|
'attr' => 'default_document_class', |
135 |
|
|
'type' => 'string', |
136 |
|
|
'rusname' => 'Класс документов в секции, показываемый по умолчанию', |
137 |
|
|
}, |
138 |
|
|
{ |
139 |
|
|
'attr' => 'default_table_class', |
140 |
|
|
'type' => 'string', |
141 |
|
|
'rusname' => 'Класс таблицы, документы которой будут показаны по умолчанию', |
142 |
|
|
}, |
143 |
|
|
{ |
144 |
|
|
'attr' => 'order_by', |
145 |
|
|
'type' => 'string', |
146 |
|
|
'rusname' => 'Сортировка документов', |
147 |
|
|
}, |
148 |
|
|
{ |
149 |
118 |
ahitrov |
'attr' => 'no_count', |
150 |
|
|
'type' => 'checkbox', |
151 |
|
|
'rusname' => 'Не пересчитывать документы в разделе админки', |
152 |
|
|
}, |
153 |
|
|
{ |
154 |
8 |
ahitrov@rambler.ru |
'attr' => 'filters', |
155 |
|
|
'type' => 'struct', |
156 |
|
|
'rusname' => 'Дополнительные фильтры выборки', |
157 |
|
|
}, |
158 |
|
|
); |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
# Конструктор для создания корневной секции |
162 |
|
|
sub root { |
163 |
|
|
return new(shift, shift, $ROOT); |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
|
167 |
|
|
# Заглушка для метода parent |
168 |
|
|
sub parent { |
169 |
|
|
return shift->pid(@_); |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
# ---------------------------------------------------------------------------- |
173 |
|
|
# Удаление секции. Перед удалением происходит проверка - а имеем |
174 |
|
|
# ли мы право удалять (может есть детишки). Если есть детишки, то секция |
175 |
|
|
# не удаляется. Проверка на наличие объектов в этой секции не производится, |
176 |
|
|
# но секция вычитается из всех сообщений. |
177 |
|
|
# ---------------------------------------------------------------------------- |
178 |
|
|
sub delete |
179 |
|
|
{ |
180 |
|
|
my $self = shift; |
181 |
|
|
do { $log->error("Метод ->delete() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
182 |
|
|
do { $log->warning("Вызов метода ->delete() без указания идентификатора для удаления"); return undef } unless ($self->{id}); |
183 |
|
|
|
184 |
|
|
# Проверка наличия детей... |
185 |
|
|
my ($one_id) = $self->keeper->SQL->selectrow_array('select id from '.$self->class_table->db_table.' where pid = ?', {}, $self->id); |
186 |
|
|
if (defined($one_id) && ($one_id > 0)) { return "Нельзя удалить секцию, у которой есть вложенные секции\n"; }; |
187 |
|
|
|
188 |
|
|
$self->SUPER::delete(); |
189 |
|
|
|
190 |
|
|
return 1; |
191 |
|
|
} |
192 |
|
|
|
193 |
|
|
|
194 |
|
|
|
195 |
|
|
# ---------------------------------------------------------------------------- |
196 |
|
|
# Метод, возвращающий массив детишек (в порядке sorder для |
197 |
|
|
# каждого уровня). В параметре передается глубина. |
198 |
|
|
# |
199 |
|
|
# Формат вызова: |
200 |
|
|
# $section->childs([глубина]); |
201 |
|
|
# ---------------------------------------------------------------------------- |
202 |
|
|
sub childs { |
203 |
|
|
my ($self, $depth) = @_; |
204 |
|
|
do { $log->error("Метод ->childs() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
205 |
|
|
|
206 |
|
|
# Глубина по умолчанию - 1 |
207 |
|
|
$depth ||= 1; |
208 |
|
|
|
209 |
|
|
my $SIDS = []; |
210 |
|
|
my $NEW_SIDS = [$self->id]; |
211 |
|
|
|
212 |
|
|
#пока не достигнута нужная глубина и пока нашлись новые дети |
213 |
|
|
while ($depth>0) { |
214 |
|
|
$NEW_SIDS = $self->keeper->get_sections(s=>$NEW_SIDS, ids=>1, return_mode=>'array_ref', order_by=>'pid, sorder'); |
215 |
|
|
if (ref($NEW_SIDS) and @$NEW_SIDS) { |
216 |
|
|
push (@$SIDS, @$NEW_SIDS); |
217 |
|
|
} else { |
218 |
|
|
last; |
219 |
|
|
} |
220 |
|
|
$depth--; |
221 |
|
|
} |
222 |
|
|
return @$SIDS; |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
|
226 |
|
|
|
227 |
|
|
# ---------------------------------------------------------------------------- |
228 |
|
|
# Метод для перемещение секции вверх/вниз по рубрикатору (изменение |
229 |
|
|
# sorder)... |
230 |
|
|
# |
231 |
|
|
# Формат вызова: |
232 |
|
|
# $section->move($direction); Направление задается строкой 'up'/'down' |
233 |
|
|
# ---------------------------------------------------------------------------- |
234 |
|
|
sub move { |
235 |
|
|
my ($self, $direction) = @_; |
236 |
|
|
do { $log->error("Метод ->move() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
237 |
|
|
|
238 |
|
|
return undef if ($self->keeper->state->readonly()); |
239 |
|
|
|
240 |
|
|
my $keeper = $self->keeper; |
241 |
|
|
do { $log->error("В объекте секции не определена ссылка на базу данных"); die } unless ref($keeper); |
242 |
|
|
do { $log->warning("Вызов метода ->move() без указания идентификатора секции"); return undef } |
243 |
|
|
unless (exists($self->{id}) && ($self->{id} > 0)); |
244 |
|
|
do { $log->warning("Вызов метода ->move() без указания порядка сортировки (sorder)"); return undef } |
245 |
|
|
unless (exists($self->{sorder}) && ($self->{sorder} >= 0)); |
246 |
|
|
do { $log->warning("Вызов метода ->childs() без указания родителя"); return undef } unless (exists($self->{pid}) && ($self->{pid} >= 0)); |
247 |
|
|
|
248 |
|
|
$direction = lc($direction); |
249 |
|
|
if ( ($direction ne 'up') && ($direction ne 'down') ) { $log->warning("Направление перемещения секции задано неверено"); return undef }; |
250 |
|
|
|
251 |
|
|
|
252 |
|
|
$keeper->t_connect() || do { $keeper->error(); return undef; }; |
253 |
|
|
$keeper->TSQL->begin_work(); |
254 |
|
|
|
255 |
|
|
|
256 |
|
|
# Получение соседней секции для обмена... |
257 |
|
|
my ($id_, $sorder_); |
258 |
|
|
if ($direction eq 'up') |
259 |
|
|
{ |
260 |
|
|
($id_, $sorder_) = $keeper->TSQL->selectrow_array("select id, sorder from ".$self->class_table->db_table." where sorder < ? and pid = ? order by sorder desc limit 1", {}, $self->{sorder}, $self->{pid}); |
261 |
|
|
} else { |
262 |
|
|
($id_, $sorder_) = $keeper->TSQL->selectrow_array("select id, sorder from ".$self->class_table->db_table." where sorder > ? and pid = ? order by sorder asc limit 1", {}, $self->{sorder}, $self->{pid}); |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
|
266 |
|
|
# Собственно обмен... |
267 |
|
|
if ( defined($id_) && ($id_ > 0) && defined($sorder_) && ($sorder_ > 0) ) |
268 |
|
|
{ |
269 |
|
|
$keeper->TSQL->do("update ".$self->class_table->db_table." set sorder = ? where id = ?", {}, $sorder_, $self->{id}) |
270 |
|
|
|| return $keeper->t_abort(); |
271 |
|
|
$keeper->TSQL->do("update ".$self->class_table->db_table." set sorder = ? where id = ?", {}, $self->{sorder}, $id_) |
272 |
|
|
|| return $keeper->t_abort(); |
273 |
|
|
} else { |
274 |
|
|
$log->warning("Не могу поменяться с элементом (он неверно оформлен или его нет)"); return 2; |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
$keeper->t_finish(); |
278 |
|
|
$self->{sorder} = $sorder_; |
279 |
|
|
return 1; |
280 |
|
|
} |
281 |
|
|
|
282 |
|
|
|
283 |
|
|
|
284 |
|
|
# ---------------------------------------------------------------------------- |
285 |
|
|
# Метод для перемещения документа с id = $doc_id вверх/вниз |
286 |
|
|
# по порядку сортировки (в пределах текущей секции)... |
287 |
|
|
# |
288 |
|
|
# Формат вызова: |
289 |
|
|
# $doc->dmove($doc_id, $direction); Направление задается строкой 'up'/'down' |
290 |
|
|
# ---------------------------------------------------------------------------- |
291 |
|
|
sub dmove { |
292 |
|
|
my ($self, $doc_id, $direction) = @_; |
293 |
|
|
do { $log->error("Метод ->dmove() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
294 |
|
|
|
295 |
|
|
return undef if ($self->keeper->state->readonly()); |
296 |
|
|
|
297 |
|
|
my $keeper = $self->keeper; |
298 |
|
|
do { $log->error("В объекте не определена ссылка на базу данных"); die } unless ref($keeper); |
299 |
|
|
do { $log->warning("Вызов метода ->dmove() без указания идентификатора секции"); return undef } |
300 |
|
|
unless (exists($self->{id}) && ($self->{id} > 0)); |
301 |
|
|
|
302 |
|
|
$direction = lc($direction); |
303 |
|
|
if ( ($direction ne 'up') && ($direction ne 'down') ) { $log->warning("Направление перемещения документа задано неверно"); return undef }; |
304 |
|
|
|
305 |
|
|
my $sorder_; |
306 |
|
|
if ($self->_sorted()) { |
307 |
282 |
ahitrov |
my @ids = $keeper->get_documents( ids =>1, s => $self->id(), |
308 |
|
|
($self->default_document_class ? (class => $self->default_document_class) : $self->default_table_class ? (table => $self->default_table_class) : ()), |
309 |
|
|
order => ['date', undef], light => 1 |
310 |
|
|
); |
311 |
8 |
ahitrov@rambler.ru |
my %ids = map { $_ => 1 } @ids; |
312 |
|
|
unless ($self->{_sorted_order}) { |
313 |
|
|
$self->{_sorted_order} = join ',', @ids; |
314 |
|
|
} |
315 |
|
|
|
316 |
|
|
my @order = split(/,/, $self->{_sorted_order}); |
317 |
|
|
@order = grep { |
318 |
|
|
my $res; |
319 |
|
|
if (exists $ids{$_}) { |
320 |
|
|
$res = 1; |
321 |
|
|
delete $ids{$_}; |
322 |
|
|
} |
323 |
|
|
$res |
324 |
|
|
} @order; |
325 |
|
|
|
326 |
|
|
push @order, keys %ids; |
327 |
|
|
|
328 |
|
|
foreach my $i (0 .. $#order) { |
329 |
|
|
if ($order[$i] == $doc_id) { |
330 |
|
|
my $t; |
331 |
|
|
if ($direction eq 'up') { |
332 |
|
|
last if $i == 0; |
333 |
|
|
$t = $order[$i-1]; |
334 |
|
|
$order[$i-1] = $order[$i]; |
335 |
|
|
$order[$i] = $t; |
336 |
|
|
$sorder_ = $i - 1; |
337 |
|
|
last; |
338 |
|
|
} elsif ($direction eq 'down') { |
339 |
|
|
last if $i == $#order; |
340 |
|
|
$t = $order[$i+1]; |
341 |
|
|
$order[$i+1] = $order[$i]; |
342 |
|
|
$order[$i] = $t; |
343 |
|
|
$sorder_ = $i + 1; |
344 |
|
|
last; |
345 |
|
|
} |
346 |
|
|
} |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
$self->{_sorted_order} = join ',', @order; |
350 |
|
|
$self->store(); |
351 |
|
|
} else { |
352 |
|
|
$log->warning("dmove called for section without enabled sorted feature... $self->{id}/$self->{class}"); |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
$self->{sorder} = $sorder_; |
356 |
|
|
return 1; |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
|
360 |
|
|
|
361 |
|
|
|
362 |
|
|
# ---------------------------------------------------------------------------- |
363 |
|
|
# Метод для построения пути между двумя рубриками... Возвращает |
364 |
|
|
# массив идентификаторов секций от рубрики секции до $root_id снизу |
365 |
|
|
# вверх. $root_id обязательно должен быть выше по рубрикатору. В результат |
366 |
|
|
# включаются обе стороны. Если рубрики равны, то возвращается массив из |
367 |
|
|
# одного элемента, если пути нет - то пустой массив. |
368 |
|
|
# |
369 |
|
|
# Формат вызова: |
370 |
|
|
# $section->trace($root_id) |
371 |
|
|
# ---------------------------------------------------------------------------- |
372 |
|
|
sub trace { |
373 |
|
|
my ($self, $root_id) = @_; |
374 |
|
|
do { $log->error("Метод ->trace() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
375 |
|
|
|
376 |
|
|
do { $log->warning("Вызов метода ->trace() без указания идентификатора секции"); return () } |
377 |
|
|
unless (exists($self->{id}) && ($self->{id} > 0)); |
378 |
|
|
$root_id ||= $ROOT; |
379 |
|
|
|
380 |
|
|
my $id_ = $self->{id}; |
381 |
|
|
my @SIDS = ($id_); |
382 |
|
|
my $sth = $self->keeper->SQL->prepare_cached("select pid from ".$self->class_table->db_table." where id = ?"); |
383 |
|
|
|
384 |
|
|
while ($id_ != $root_id) |
385 |
|
|
{ |
386 |
|
|
$sth->execute($id_); |
387 |
|
|
($id_) = $sth->fetchrow_array(); |
388 |
|
|
if (defined($id_) && ($id_ > 0)) |
389 |
|
|
{ |
390 |
|
|
unshift (@SIDS, $id_); |
391 |
|
|
} else { |
392 |
|
|
# Мы закочили путешествие вверх по рубрикам, а до корня не дошли... |
393 |
|
|
$sth->finish; |
394 |
|
|
return (); |
395 |
|
|
} |
396 |
|
|
} |
397 |
|
|
$sth->finish; |
398 |
|
|
return @SIDS; |
399 |
|
|
} |
400 |
|
|
|
401 |
|
|
|
402 |
|
|
# ---------------------------------------------------------------------------- |
403 |
|
|
# Предки |
404 |
|
|
# Возвращает массив идентификаторов всех предков (родителей на всех уровнях) данной секции |
405 |
|
|
# ---------------------------------------------------------------------------- |
406 |
|
|
sub ancestors |
407 |
|
|
{ |
408 |
|
|
my $self = shift; |
409 |
|
|
do { $log->error("Метод ->ancestors() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
410 |
|
|
|
411 |
|
|
my $keeper = $self->keeper; |
412 |
|
|
do { $log->error("В объекте секции не определена ссылка на базу данных"); die } unless ref($keeper); |
413 |
|
|
|
414 |
|
|
do { $log->warning("Вызов метода ->ancestors() без указания идентификатора секции"); return () } unless (exists($self->{id}) && ($self->{id} > 0)); |
415 |
|
|
|
416 |
|
|
my @ancestors = (); |
417 |
|
|
my $sectionid = $self->{id}; |
418 |
|
|
while ($sectionid) |
419 |
|
|
{ |
420 |
|
|
$sectionid = $keeper->SQL->selectrow_array("select pid from ".$self->class_table->db_table." where id = ?", {}, $sectionid); |
421 |
|
|
push @ancestors, $sectionid if defined $sectionid && $sectionid; |
422 |
|
|
} |
423 |
|
|
return @ancestors; |
424 |
|
|
} |
425 |
|
|
|
426 |
|
|
# ---------------------------------------------------------------------------- |
427 |
|
|
# Потомки |
428 |
|
|
# Возвращает массив идентификаторов всех потомков (детей на всех уровнях) данной секции |
429 |
|
|
# ---------------------------------------------------------------------------- |
430 |
|
|
sub descendants |
431 |
|
|
{ |
432 |
|
|
my $self = shift; |
433 |
|
|
do { $log->error("Метод ->descendants() можно вызывать только у объектов, но не классов"); die } unless ref($self); |
434 |
|
|
|
435 |
|
|
my $keeper = $self->keeper; |
436 |
|
|
do { $log->error("В объекте секции не определена ссылка на базу данных"); die } unless ref($keeper); |
437 |
|
|
|
438 |
|
|
do { $log->warning("Вызов метода ->descendants() без указания идентификатора секции"); return () } unless (exists($self->{id}) && ($self->{id} > 0)); |
439 |
|
|
|
440 |
|
|
my @descendants = (); |
441 |
|
|
my @ids = ($self->{id}); |
442 |
|
|
while (scalar @ids) |
443 |
|
|
{ |
444 |
|
|
my $sth = $keeper->SQL->prepare("select id from ".$self->class_table->db_table." where pid in (" . (join ", ", @ids) . ")"); |
445 |
|
|
$sth->execute; |
446 |
|
|
@ids = (); |
447 |
|
|
while (my ($id) = $sth->fetchrow_array) |
448 |
|
|
{ |
449 |
|
|
push @ids, $id; |
450 |
|
|
} |
451 |
|
|
$sth->finish(); |
452 |
|
|
push @descendants, @ids; |
453 |
|
|
last if !$sth->rows; |
454 |
|
|
} |
455 |
|
|
return @descendants; |
456 |
|
|
} |
457 |
|
|
|
458 |
|
|
# ------------------------------------------------------------------------------------------------- |
459 |
|
|
# Получение деревца... |
460 |
|
|
# Параметры: |
461 |
|
|
# light => облегченная версия |
462 |
|
|
# root => корень дерева (по умолчанию - 1) |
463 |
|
|
# ------------------------------------------------------------------------------------------------- |
464 |
|
|
sub get_tree { |
465 |
|
|
my ($self, %opts) = @_; |
466 |
|
|
do { $log->warning("Метод ->get_tree() можно вызывать только у объектов, но не классов"); return undef } unless ref($self); |
467 |
|
|
|
468 |
|
|
my $root = $opts{root} || $ROOT; |
469 |
|
|
|
470 |
|
|
|
471 |
|
|
# ---------------------------------------------------------------------------------------- |
472 |
|
|
# Выбираем все секции |
473 |
|
|
$opts{no_limit} = 1; |
474 |
|
|
delete $opts{root}; |
475 |
|
|
my @sections = $self->keeper->get_sections(%opts); |
476 |
|
|
|
477 |
|
|
my $CACHE = {}; |
478 |
|
|
foreach my $section (@sections) { |
479 |
|
|
if (ref($section)) { |
480 |
|
|
$CACHE->{$section->id()} = $section; |
481 |
|
|
} |
482 |
|
|
} |
483 |
|
|
|
484 |
|
|
for my $id (sort { $CACHE->{$a}->sorder() <=> $CACHE->{$b}->sorder() } (keys(%{ $CACHE }))) { |
485 |
|
|
my $pid = $CACHE->{$id}->pid() || ''; |
486 |
|
|
$CACHE->{$pid}->{childs} = [] if (! exists($CACHE->{$pid}->{childs})); |
487 |
|
|
$CACHE->{$id}->{parent} = $CACHE->{$pid}; |
488 |
|
|
push (@{ $CACHE->{$pid}->{childs} }, $CACHE->{$id} ); |
489 |
|
|
} |
490 |
|
|
|
491 |
|
|
return $CACHE->{$root}; |
492 |
|
|
} |
493 |
|
|
|
494 |
|
|
1; |
495 |
|
|
|