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 |
|
|
|