Line # Revision Author
1 8 ahitrov@rambler.ru package Contenido::Project;
2
3 # ----------------------------------------------------------------------------
4 # Contenido::Project - класс, который будет содержать перечень полей с
5 # общей информацией о проекте (ширина, основные цвета, общее название
6 # для title's и так далее). Объект этого класса будет создаваться при
7 # старте Apache-сервера. Данные он будет брать из специального хэша
8 # (который возможно будет редактироваться через редакторский интерфейс)
9 # и перечитывать их каждый раз при измении файла на диске.
10 # ----------------------------------------------------------------------------
11
12 use strict;
13 use warnings;
14 use locale;
15
16 use vars qw($VERSION $AUTOLOAD);
17 $VERSION = '1.0';
18
19
20 use Utils;
21 use Contenido::Globals;
22 use Data::Dumper;
23
24 # ----------------------------------------------------------------------------
25 # Конструктор. Создает объект с описанием проекта.
26 #
27 # Формат вызова:
28 # Contenido::Project->new();
29 # ----------------------------------------------------------------------------
30 sub new
31 {
32 my ($proto, $state, $local_keeper) = @_;
33 unless ( ref $state ) {
34 $log->error("Неправильный вызов конструктора объекта проекта. В параметрах нет объекта класса Contenido::State.");
35 die;
36 }
37
38 $local_keeper ||= $keeper;
39
40 my $class = ref($proto) || $proto;
41 my $self = {};
42 bless($self, $class);
43 $self->_init_;
44
45 # Запомним то, что нас интересует...
46 $self->{state} = $state;
47 $self->{project_name} = $state->project_name();
48 $self->{debug} = $state->debug();
49 # $self->{mtime} = 0;
50 $self->{data} = {};
51
52 $self->restore($local_keeper);
53 return $self;
54 }
55
56
57
58 # ------------------------------------------------------------------------------------------------
59 # Инициализация внутренних структур
60 # ------------------------------------------------------------------------------------------------
61 sub _init_
62 {
63 my $self = shift;
64 $self->{attributes} = {};
65 foreach my $a ( qw(project_name mtime debug) )
66 {
67 $self->{attributes}->{ $a } = 'SCALAR';
68 }
69 return 1;
70 }
71
72
73
74
75 # ------------------------------------------------------------------------------------------------
76 # Метод restore() для заполнения объекта Project данными...
77 #
78 # Формат использования:
79 # $project->restore()
80 # ------------------------------------------------------------------------------------------------
81 sub restore
82 {
83 my $self = shift;
84 my $local_keeper = shift;
85 my $force = shift;
86 do { $log->error("Метод restore() можно вызывать только у объектов, но не классов"); die } unless ref $self;
87 do { $log->error("Метод restore() require established connection to DB"); die } unless (ref($local_keeper));
88
89 return 1 if (!$force and $self->{"mtime"} and $self->{"mtime"} > time - $state->{"options_expire"});
90
91 #TODO: implement hardcoded project attributes if db_type eq 'none'
92 return 1 if $self->{state}->db_type eq 'none';
93
94 delete $self->{"data"};
95 delete $self->{"attributes"};
96
97 my $sth = $local_keeper->SQL->prepare_cached("SELECT id, pid, name, value, type FROM options", {}, 1) || do { $log->error("Not connected to DB"); die };
98 $sth->execute();
99
100 my $data_ref = $sth->fetchall_arrayref() || [];
101 $sth->finish();
102 foreach (@$data_ref) {
103 $_->[1] = 0 unless ($_->[1]);
104 }
105
106 $self->{data} = restore_recursive('HASH',$data_ref, 0);
107
108 foreach my $key (keys %{$self->{"data"}}) {
109 $self->{"attributes"}{$key} = "DATA";
110 }
111
112 $self->{"mtime"} = time;
113
114 return 1;
115 }
116
117 sub restore_recursive {
118 my $type = shift;
119 my $data_ref = shift;
120 my $pid = shift;
121
122 my $struct_ref;
123
124 if ($type eq 'ARRAY') {
125 $struct_ref = [];
126 foreach my $item (@$data_ref) {
127 my ($id, $local_pid, $local_name, $local_value, $local_type) = @$item;
128 next unless ($local_pid == $pid);
129 if ($local_type eq 'SCALAR') {
130 $struct_ref->[$local_name] = $local_value;
131 } else {
132 $struct_ref->[$local_name] = restore_recursive($local_type, $data_ref, $id);
133 }
134 }
135 } elsif ($type eq 'HASH') {
136 $struct_ref = {};
137 foreach my $item (@$data_ref) {
138 my ($id, $local_pid, $local_name, $local_value, $local_type) = @$item;
139 next unless ($local_pid == $pid);
140 if ($local_type eq 'SCALAR') {
141 $struct_ref->{$local_name} = $local_value;
142 } else {
143 $struct_ref->{$local_name} = restore_recursive($local_type, $data_ref, $id);
144 }
145 }
146 } elsif ($type eq 'SCALAR') {
147 do { $log->error("Recursive call with scalar value... "); die };
148 } else {
149 do { $log->error("Wrong type in call '$type'"); die };
150 }
151
152 return $struct_ref;
153 }
154
155 # ----------------------------------------------------------------------------
156 # Метод store() для сохранения объекта Project на диск... то есть в БД
157 #
158 # Формат использования:
159 # $project->store()
160 # ----------------------------------------------------------------------------
161
162 sub store
163 {
164 my $self = shift;
165 my $local_keeper = shift;
166
167 do { $log->error("Метод store() можно вызывать только у объектов, но не классов"); die } unless ref($self);
168 do { $log->error("Метод store() require established connection to DB"); die } unless (ref($keeper) and $keeper->is_connected());
169
170 return 1 if $self->{state}->db_type eq 'none';
171
172 $local_keeper->t_connect() || do { $local_keeper->error(); return undef; };
173 my $insert = $keeper->TSQL->prepare("INSERT INTO options (pid, name, value, type) VALUES (?, ?, ?, ?)");
174 $local_keeper->TSQL->do("DELETE FROM options");
175 store_recursive(undef, undef, $self->{"data"}, $insert, $local_keeper);
176 $insert->finish();
177 $local_keeper->t_finish();
178
179 undef $self->{"mtime"};
180
181 return 1;
182 }
183
184 # TODO переделать нафиг ужос
185 sub store_recursive {
186 my $pid = shift;
187 my $key = shift;
188 my $data = shift;
189 my $insert = shift;
190 my $local_keeper = shift;
191
192 my $new_pid;
193
194 if (ref $data eq "HASH") {
195 if (defined $key) {
196 $insert->execute($pid, $key, undef, "HASH");
197 $new_pid = $local_keeper->TSQL->selectrow_array("SELECT currval('documents_id_seq')");
198 }
199
200 foreach my $new_key (keys %$data) {
201 store_recursive($new_pid, $new_key, $data->{$new_key}, $insert, $local_keeper);
202 }
203 } elsif (ref $data eq "ARRAY") {
204 if (defined $key) {
205 $insert->execute($pid, $key, undef, "ARRAY");
206 $new_pid = $local_keeper->TSQL->selectrow_array("SELECT currval('documents_id_seq')");
207 }
208
209 foreach my $new_key (0 .. $#$data) {
210 store_recursive($new_pid, $new_key, $data->[$new_key], $insert, $local_keeper);
211 }
212 } else {
213 $insert->execute($pid, $key, $data, "SCALAR") || return $local_keeper->t_abort();
214 }
215 }
216
217 # ----------------------------------------------------------------------------
218 # Это умный AUTOLOAD. Ловит методов для установки/чтения полей...
219 # Версия 0.2/Модифицированная версия
220 # ----------------------------------------------------------------------------
221
222 sub AUTOLOAD
223 {
224 my $self = shift;
225 my $attribute = $AUTOLOAD;
226
227
228 $attribute =~ s/.*:://;
229 return undef unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY
230
231
232 if (! exists($self->{attributes}->{$attribute}))
233 {
234 $log->error("Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute(). Создаем запись.");
235 $self->{attributes}->{$attribute} = 'DATA';
236 }
237
238 if ($self->{attributes}->{$attribute} eq 'DATA')
239 {
240 $self->{data}->{ $attribute } = shift @_ if (scalar(@_) > 0);
241 return $self->{data}->{ $attribute };
242 } else {
243 $self->{ $attribute } = shift @_ if (scalar(@_) > 0);
244 return $self->{ $attribute };
245 }
246 }
247
248 1;
249