Line # Revision Author
1 3 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 #��������� ������ store
68 sub store {
69 my $self=shift;
70
71 #��� ������������� ������ ������ ����� sorder
72 unless ($self->{id}) {
73 my ($sorder) = $self->keeper->SQL->selectrow_array("select max(sorder) from ".$self->class_table->db_table(), {});
74 $self->{sorder} = $sorder + 1;
75 }
76
77 return $self->SUPER::store();
78 }
79
80
81 ## ����������� �������� ��� ������ � ���������� ����������� ����������
82 sub add_properties {
83 return (
84 { # ������� "������ � �����������"
85 'attr' => '_sorted',
86 'type' => 'checkbox',
87 'rusname' => '������ ���������� ����������',
88 },
89 { # ������� ���������� (������ id)
90 'attr' => '_sorted_order',
91 'type' => 'string',
92 'rusname' => '������� ���������� � ������',
93 'hidden' => 1
94 },
95 {
96 'attr' => 'default_document_class',
97 'type' => 'string',
98 'rusname' => '����� ���������� � ������, ������������ �� ���������',
99 },
100 {
101 'attr' => 'default_table_class',
102 'type' => 'string',
103 'rusname' => '����� �������, ��������� ������� ����� �������� �� ���������',
104 },
105 {
106 'attr' => 'order_by',
107 'type' => 'string',
108 'rusname' => '���������� ����������',
109 },
110 {
111 'attr' => 'no_count',
112 'type' => 'checkbox',
113 'rusname' => '�� ������������� ��������� � ������� �������',
114 },
115 {
116 'attr' => 'filters',
117 'type' => 'struct',
118 'rusname' => '�������������� ������� �������',
119 },
120 );
121 }
122
123 # ����������� ��� �������� ��������� ������
124 sub root {
125 return new(shift, shift, $ROOT);
126 }
127
128
129 # �������� ��� ������ parent
130 sub parent {
131 return shift->pid(@_);
132 }
133
134 # ----------------------------------------------------------------------------
135 # �������� ������. ����� ��������� ���������� �������� - � �����
136 # �� �� ����� ������� (����� ���� �������). ���� ���� �������, �� ������
137 # �� ���������. �������� �� ������� �������� � ���� ������ �� ������������,
138 # �� ������ ���������� �� ���� ���������.
139 # ----------------------------------------------------------------------------
140 sub delete
141 {
142 my $self = shift;
143 do { $log->error("����� ->delete() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
144 do { $log->warning("����� ������ ->delete() ��� �������� �������������� ��� ��������"); return undef } unless ($self->{id});
145
146 # �������� ������� �����...
147 my ($one_id) = $self->keeper->SQL->selectrow_array('select id from '.$self->class_table->db_table.' where pid = ?', {}, $self->id);
148 if (defined($one_id) && ($one_id > 0)) { return "������ ������� ������, � ������� ���� ��������� ������\n"; };
149
150 $self->SUPER::delete();
151
152 return 1;
153 }
154
155
156
157 # ----------------------------------------------------------------------------
158 # �����, ������������ ������ ������� (� ������� sorder ���
159 # ������� ������). � ��������� ���������� �������.
160 #
161 # ������ ������:
162 # $section->childs([�������]);
163 # ----------------------------------------------------------------------------
164 sub childs {
165 my ($self, $depth) = @_;
166 do { $log->error("����� ->childs() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
167
168 # ������� �� ��������� - 1
169 $depth ||= 1;
170
171 my $SIDS = [];
172 my $NEW_SIDS = [$self->id];
173
174 #���� �� ���������� ������ ������� � ���� ������� ����� ����
175 while ($depth>0) {
176 $NEW_SIDS = $self->keeper->get_sections(s=>$NEW_SIDS, ids=>1, return_mode=>'array_ref', order_by=>'pid, sorder');
177 if (ref($NEW_SIDS) and @$NEW_SIDS) {
178 push (@$SIDS, @$NEW_SIDS);
179 } else {
180 last;
181 }
182 $depth--;
183 }
184 return @$SIDS;
185 }
186
187
188
189 # ----------------------------------------------------------------------------
190 # ����� ��� ����������� ������ �����/���� �� ����������� (���������
191 # sorder)...
192 #
193 # ������ ������:
194 # $section->move($direction); ����������� �������� ������� 'up'/'down'
195 # ----------------------------------------------------------------------------
196 sub move {
197 my ($self, $direction) = @_;
198 do { $log->error("����� ->move() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
199
200 return undef if ($self->keeper->state->readonly());
201
202 my $keeper = $self->keeper;
203 do { $log->error("� ������� ������ �� ���������� ������ �� ���� ������"); die } unless ref($keeper);
204 do { $log->warning("����� ������ ->move() ��� �������� �������������� ������"); return undef }
205 unless (exists($self->{id}) && ($self->{id} > 0));
206 do { $log->warning("����� ������ ->move() ��� �������� ������� ���������� (sorder)"); return undef }
207 unless (exists($self->{sorder}) && ($self->{sorder} >= 0));
208 do { $log->warning("����� ������ ->childs() ��� �������� ��������"); return undef } unless (exists($self->{pid}) && ($self->{pid} >= 0));
209
210 $direction = lc($direction);
211 if ( ($direction ne 'up') && ($direction ne 'down') ) { $log->warning("����������� ����������� ������ ������ ��������"); return undef };
212
213
214 $keeper->t_connect() || do { $keeper->error(); return undef; };
215 $keeper->TSQL->begin_work();
216
217
218 # ��������� �������� ������ ��� ������...
219 my ($id_, $sorder_);
220 if ($direction eq 'up')
221 {
222 ($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});
223 } else {
224 ($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});
225 }
226
227
228 # ���������� �����...
229 if ( defined($id_) && ($id_ > 0) && defined($sorder_) && ($sorder_ > 0) )
230 {
231 $keeper->TSQL->do("update ".$self->class_table->db_table." set sorder = ? where id = ?", {}, $sorder_, $self->{id})
232 || return $keeper->t_abort();
233 $keeper->TSQL->do("update ".$self->class_table->db_table." set sorder = ? where id = ?", {}, $self->{sorder}, $id_)
234 || return $keeper->t_abort();
235 } else {
236 $log->warning("�� ���� ���������� � ��������� (�� ������� �������� ��� ��� ���)"); return 2;
237 }
238
239 $keeper->t_finish();
240 $self->{sorder} = $sorder_;
241 return 1;
242 }
243
244
245
246 # ----------------------------------------------------------------------------
247 # ����� ��� ����������� ��������� � id = $doc_id �����/����
248 # �� ������� ���������� (� �������� ������� ������)...
249 #
250 # ������ ������:
251 # $doc->dmove($doc_id, $direction); ����������� �������� ������� 'up'/'down'
252 # ----------------------------------------------------------------------------
253 sub dmove {
254 my ($self, $doc_id, $direction) = @_;
255 do { $log->error("����� ->dmove() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
256
257 return undef if ($self->keeper->state->readonly());
258
259 my $keeper = $self->keeper;
260 do { $log->error("� ������� �� ���������� ������ �� ���� ������"); die } unless ref($keeper);
261 do { $log->warning("����� ������ ->dmove() ��� �������� �������������� ������"); return undef }
262 unless (exists($self->{id}) && ($self->{id} > 0));
263
264 $direction = lc($direction);
265 if ( ($direction ne 'up') && ($direction ne 'down') ) { $log->warning("����������� ����������� ��������� ������ �������"); return undef };
266
267 my $sorder_;
268 if ($self->_sorted()) {
269 my @ids = $keeper->get_documents( ids =>1, s => $self->id(), ($self->default_document_class ? (class => $self->default_document_class) : ()), order => ['date', undef], light => 1);
270 my %ids = map { $_ => 1 } @ids;
271 unless ($self->{_sorted_order}) {
272 $self->{_sorted_order} = join ',', @ids;
273 }
274
275 my @order = split(/,/, $self->{_sorted_order});
276 @order = grep {
277 my $res;
278 if (exists $ids{$_}) {
279 $res = 1;
280 delete $ids{$_};
281 }
282 $res
283 } @order;
284
285 push @order, keys %ids;
286
287 foreach my $i (0 .. $#order) {
288 if ($order[$i] == $doc_id) {
289 my $t;
290 if ($direction eq 'up') {
291 last if $i == 0;
292 $t = $order[$i-1];
293 $order[$i-1] = $order[$i];
294 $order[$i] = $t;
295 $sorder_ = $i - 1;
296 last;
297 } elsif ($direction eq 'down') {
298 last if $i == $#order;
299 $t = $order[$i+1];
300 $order[$i+1] = $order[$i];
301 $order[$i] = $t;
302 $sorder_ = $i + 1;
303 last;
304 }
305 }
306 }
307
308 $self->{_sorted_order} = join ',', @order;
309 $self->store();
310 } else {
311 $log->warning("dmove called for section without enabled sorted feature... $self->{id}/$self->{class}");
312 }
313
314 $self->{sorder} = $sorder_;
315 return 1;
316 }
317
318
319
320
321 # ----------------------------------------------------------------------------
322 # ����� ��� ���������� ���� ����� ����� ���������... ����������
323 # ������ ��������������� ������ �� ������� ������ �� $root_id �����
324 # �����. $root_id ����������� ������ ���� ���� �� �����������. � ���������
325 # ���������� ��� �������. ���� ������� �����, �� ������������ ������ ��
326 # ������ ��������, ���� ���� ��� - �� ������ ������.
327 #
328 # ������ ������:
329 # $section->trace($root_id)
330 # ----------------------------------------------------------------------------
331 sub trace {
332 my ($self, $root_id) = @_;
333 do { $log->error("����� ->trace() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
334
335 do { $log->warning("����� ������ ->trace() ��� �������� �������������� ������"); return () }
336 unless (exists($self->{id}) && ($self->{id} > 0));
337 $root_id ||= $ROOT;
338
339 my $id_ = $self->{id};
340 my @SIDS = ($id_);
341 my $sth = $self->keeper->SQL->prepare_cached("select pid from ".$self->class_table->db_table." where id = ?");
342
343 while ($id_ != $root_id)
344 {
345 $sth->execute($id_);
346 ($id_) = $sth->fetchrow_array();
347 if (defined($id_) && ($id_ > 0))
348 {
349 unshift (@SIDS, $id_);
350 } else {
351 # �� �������� ����������� ����� �� ��������, � �� ����� �� �����...
352 $sth->finish;
353 return ();
354 }
355 }
356 $sth->finish;
357 return @SIDS;
358 }
359
360
361 # ----------------------------------------------------------------------------
362 # ������
363 # ���������� ������ ��������������� ���� ������� (��������� �� ���� �������) ������ ������
364 # ----------------------------------------------------------------------------
365 sub ancestors
366 {
367 my $self = shift;
368 do { $log->error("����� ->ancestors() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
369
370 my $keeper = $self->keeper;
371 do { $log->error("� ������� ������ �� ���������� ������ �� ���� ������"); die } unless ref($keeper);
372
373 do { $log->warning("����� ������ ->ancestors() ��� �������� �������������� ������"); return () } unless (exists($self->{id}) && ($self->{id} > 0));
374
375 my @ancestors = ();
376 my $sectionid = $self->{id};
377 while ($sectionid)
378 {
379 $sectionid = $keeper->SQL->selectrow_array("select pid from ".$self->class_table->db_table." where id = ?", {}, $sectionid);
380 push @ancestors, $sectionid if defined $sectionid && $sectionid;
381 }
382 return @ancestors;
383 }
384
385 # ----------------------------------------------------------------------------
386 # �������
387 # ���������� ������ ��������������� ���� �������� (����� �� ���� �������) ������ ������
388 # ----------------------------------------------------------------------------
389 sub descendants
390 {
391 my $self = shift;
392 do { $log->error("����� ->descendants() ����� �������� ������ � ��������, �� �� �������"); die } unless ref($self);
393
394 my $keeper = $self->keeper;
395 do { $log->error("� ������� ������ �� ���������� ������ �� ���� ������"); die } unless ref($keeper);
396
397 do { $log->warning("����� ������ ->descendants() ��� �������� �������������� ������"); return () } unless (exists($self->{id}) && ($self->{id} > 0));
398
399 my @descendants = ();
400 my @ids = ($self->{id});
401 while (scalar @ids)
402 {
403 my $sth = $keeper->SQL->prepare("select id from ".$self->class_table->db_table." where pid in (" . (join ", ", @ids) . ")");
404 $sth->execute;
405 @ids = ();
406 while (my ($id) = $sth->fetchrow_array)
407 {
408 push @ids, $id;
409 }
410 $sth->finish();
411 push @descendants, @ids;
412 last if !$sth->rows;
413 }
414 return @descendants;
415 }
416
417 # -------------------------------------------------------------------------------------------------
418 # ��������� �������...
419 # ���������:
420 # light => ����������� ������
421 # root => ������ ������ (�� ��������� - 1)
422 # -------------------------------------------------------------------------------------------------
423 sub get_tree {
424 my ($self, %opts) = @_;
425 do { $log->warning("����� ->get_tree() ����� �������� ������ � ��������, �� �� �������"); return undef } unless ref($self);
426
427 my $root = $opts{root} || $ROOT;
428
429
430 # ----------------------------------------------------------------------------------------
431 # �������� ��� ������
432 $opts{no_limit} = 1;
433 delete $opts{root};
434 my @sections = $self->keeper->get_sections(%opts);
435
436 my $CACHE = {};
437 foreach my $section (@sections) {
438 if (ref($section)) {
439 $CACHE->{$section->id()} = $section;
440 }
441 }
442
443 for my $id (sort { $CACHE->{$a}->sorder() <=> $CACHE->{$b}->sorder() } (keys(%{ $CACHE }))) {
444 my $pid = $CACHE->{$id}->pid() || '';
445 $CACHE->{$pid}->{childs} = [] if (! exists($CACHE->{$pid}->{childs}));
446 $CACHE->{$id}->{parent} = $CACHE->{$pid};
447 push (@{ $CACHE->{$pid}->{childs} }, $CACHE->{$id} );
448 }
449
450 return $CACHE->{$root};
451 }
452
453 1;
454