Revision 280
Date:
2013/02/18 17:36:00
Author:
ahitrov
Revision Log:
Plugin import
Files:
Legend:
Added
Removed
Modified
utf8/plugins/MySQL/comps/contenido/MySQL/autohandler
1
<%init>
2
3
$r->content_type('text/html');
4
$m->call_next();
5
6
</%init>
utf8/plugins/MySQL/comps/contenido/MySQL/dhandler
1
<& $call, %ARGS &>
2
<%init>
3
4
my $call;
5
if ( $r->uri eq '/contenido/MySQL/' ) {
6
$call = 'index.html';
7
} else {
8
&abort404;
9
}
10
11
</%init>
utf8/plugins/MySQL/comps/contenido/MySQL/index.html
1
<& "/contenido/components/header.msn" &>
2
<& "/contenido/components/naviline.msn" &>
3
4
<p>PLugin [MySQL]</p>
5
6
</body>
7
</html>
utf8/plugins/MySQL/config.proto
1
#############################################################################
2
#
3
# Параметры данного шаблона необходимо ВРУЧНУЮ добавить в config.mk проекта
4
# и привести в соответствие с требованиями проекта
5
#
6
#############################################################################
7
8
PLUGINS += MySQL
9
PROJECT_REQUIRED +=
10
11
MYSQL_DB_HOST =
12
MYSQL_DB_NAME =
13
MYSQL_DB_USER =
14
MYSQL_DB_PASSWORD =
15
MYSQL_DB_PORT =
16
17
REWRITE += MYSQL_DB_HOST MYSQL_DB_NAME MYSQL_DB_USER MYSQL_DB_PASSWORD MYSQL_DB_PORT
utf8/plugins/MySQL/lib/MySQL/Apache.pm
1
package MySQL::Apache;
2
3
use strict;
4
use warnings 'all';
5
6
use MySQL::State;
7
use Contenido::Globals;
8
9
10
sub child_init {
11
# встраиваем keeper плагина в keeper проекта
12
$keeper->{mysql} = MySQL::Keeper->new($state->{mysql});
13
}
14
15
sub request_init {
16
}
17
18
sub child_exit {
19
}
20
21
1;
utf8/plugins/MySQL/lib/MySQL/Globals.pm
1
package MySQL::Globals;
2
3
use strict;
4
use warnings 'all';
5
use vars qw($VERSION @ISA @EXPORT $mysql $mstate);
6
7
use Exporter;
8
@ISA = qw(Exporter);
9
@EXPORT = qw ( $mysql $mstate );
10
11
$VERSION = '1.0';
12
13
$mysql = undef;
14
$mstate = undef;
15
16
1;
utf8/plugins/MySQL/lib/MySQL/Init.pm
1
package MySQL::Init;
2
3
use strict;
4
use warnings 'all';
5
use vars qw($VERSION @ISA @EXPORT);
6
7
use Exporter;
8
@ISA = qw(Exporter);
9
@EXPORT = qw ( $mysql $mstate );
10
11
use Contenido::Globals;
12
use MySQL::Globals;
13
use MySQL::Apache;
14
use MySQL::Keeper;
15
16
# загрузка всех необходимых плагину классов
17
# MySQL::SQL::SomeTable
18
# MySQL::SomeClass
19
Contenido::Init::load_classes(qw(
20
));
21
22
sub init {
23
warn "Contenido Init: Инициализация MySQL ($$)\n" if ($DEBUG);
24
25
$mstate = MySQL::State->new();
26
$mysql = MySQL::Keeper->new($mstate);
27
28
0;
29
}
30
31
1;
utf8/plugins/MySQL/lib/MySQL/Keeper.pm
1
package MySQL::Keeper;
2
3
use strict;
4
use warnings 'all';
5
use base qw(Contenido::Keeper);
6
use locale;
7
8
use Data::Dumper;
9
use DBI;
10
use DBD::mysql;
11
use IO::File;
12
use File::Copy;
13
use Image::Size;
14
15
use Contenido::Globals;
16
use MySQL::Globals;
17
18
# ------------------------------------------------------------------------------------------------
19
# Конструктор объекта базы данных.
20
# Обязательный параметр - объект класса Contenido::State из которого
21
# конструктор возьмет необходимые параметры для доступа к БД и т.д.
22
#
23
# Формат вызова:
24
# MySQL::Keeper->new($state)
25
# ------------------------------------------------------------------------------------------------
26
sub new
27
{
28
my ($proto, $state) = @_;
29
die "Contenido Die: Неправильный вызов конструктора объекта базы данных. В параметрах нет объекта класса MySQL::State\n" unless ref($mstate);
30
31
my $class = ref($proto) || $proto;
32
my $self = {};
33
bless($self, $class);
34
35
# Заполним собственные свойства конкретными данными...
36
$self->{db_type} = 'remote';
37
$self->{db_keepalive} = 1;
38
39
$self->{db_host} = $state->db_host();
40
$self->{db_name} = $state->db_name();
41
$self->{db_user} = $state->db_user();
42
$self->{db_password} = $state->db_password();
43
$self->{db_port} = $state->db_port();
44
45
# $self->{data_dir} = $self->{data_directory} = $state->data_directory();
46
# $self->{images_dir} = $self->{images_directory} = $state->images_directory();
47
# $self->{binary_dir} = $self->{binary_directory} = $state->binary_directory();
48
49
# $self->{debug} = $state->debug();
50
# $self->{store_method} = $state->store_method();
51
# $self->{cascade} = $state->cascade();
52
53
$self->{state} = $state;
54
$self->_init_();
55
56
# соединяемся с базой если используется постоянное соединение
57
$self->connect();
58
59
return $self;
60
}
61
62
# МЕТОДЫ ДОСТУПА К СОЕДИНЕНИЯМ С БАЗОЙ УМНЫЕ
63
# получение соединения с базой или установка нового если его не было
64
sub SQL {
65
my $self = shift;
66
return ($self->connect_check() ? $self->{SQL} : undef);
67
}
68
69
70
# -------------------------------------------------------------------------------------------------
71
# Открываем соединение с базой данных
72
# -------------------------------------------------------------------------------------------------
73
sub connect {
74
my $self = shift;
75
#соединение уже есть
76
if ($self->is_connected) {
77
} else {
78
unless ($self->{SQL} = $self->db_connect) {
79
warn "Не могу соединиться с базой данных";
80
die;
81
}
82
$self->{SQL}->do("SET NAMES '".$self->state->db_client_encoding."'") if ($self->state->db_client_encoding);
83
}
84
85
$self->{_connect_ok} = 1;
86
return 1;
87
}
88
89
90
#проверка соединения с базой кеширующая состояние соединения
91
sub connect_check {
92
my $self = shift;
93
return 1 if ($self->{_connect_ok});
94
if ($self->is_connected) {
95
$self->{_connect_ok} = 1;
96
return 1;
97
} else {
98
if ($self->connect) {
99
return 1;
100
} else {
101
#сюда по логике попадать не должно так как die вылететь должен
102
warn "Connect failed\n";
103
return 0;
104
}
105
}
106
}
107
108
109
sub db_connect {
110
my $self = shift;
111
my $dbh = DBI->connect("DBI:mysql:database=".$self->{db_name}.";host=".$self->{db_host}, $self->{db_user}, $self->{db_password})
112
|| die "Contenido Error: Не могу соединиться с MySQL базой данных\n";
113
114
$dbh->{'AutoCommit'} = 1;
115
$dbh->{mysql_auto_reconnect} = 1;
116
117
return $dbh;
118
}
119
120
sub is_connected {
121
my $self = shift;
122
if (ref($self->{SQL}) and $self->{SQL}->can('ping') and $self->{SQL}->ping()) {
123
$self->{_connect_ok} = 1;
124
return 1;
125
} else {
126
$self->{_connect_ok} = 0;
127
return 0;
128
}
129
130
# warn 'Check if MySQL DB connected: '.(ref $self && exists $self->{SQL} && ref $self->{SQL} ? 1 : 0 ) if $DEBUG;
131
# return ( ref($self) && exists $self->{SQL} && ref $self->{SQL} );
132
}
133
134
# -------------------------------------------------------------------------------------------------
135
# Закрываем соединение с базой данных
136
# -------------------------------------------------------------------------------------------------
137
sub shutdown
138
{
139
my $self = shift;
140
$self->{SQL}->disconnect() if ref $self->{SQL};
141
delete $self->{SQL};
142
143
warn "SQL= ".(exists $self->{SQL} && ref $self->{SQL} ? 1 : 0) if $DEBUG;
144
warn "Contenido Debug: Закрыто соединение с базой данных MySQL на порту ".$self->{db_port}." keepalive=".$mstate->db_keepalive." .\n" if ($self->{debug});
145
}
146
147
148
# ----------------------------------------------------------------------------
149
# Инициализация.
150
# - Создает внутри объекта хэш с типами полей - это нужно для быстрой
151
# работы метода AUTOLOAD...
152
# ----------------------------------------------------------------------------
153
sub _init_
154
{
155
my $self = shift;
156
157
foreach my $attribute ( qw(
158
db_host db_name db_user db_password db_port
159
db_type db_keepalive
160
default_status
161
162
debug
163
state
164
SQL) )
165
{
166
$self->{attributes}->{ $attribute } = 'SCALAR';
167
}
168
}
169
170
171
# ----------------------------------------------------------------------------
172
# Функции работы с данными:
173
#
174
# >> get_object
175
# table => название таблицы
176
# fields => [fieldname, fieldname, ..., fieldname]
177
# wheres => список ограничений
178
# wheres => 'condition'
179
# wheres => ['condition 1', 'condition 2']
180
# wheres => { field1 => 'value1', field2 => 'value2' }
181
# values => список значений для wheres с передачей списка
182
# limit => Limit
183
# offset => Offset
184
# order_by=> Сортировка
185
# id => По ID
186
# count => Just count
187
#
188
# ----------------------------------------------------------------------------
189
sub get_object {
190
191
my $self = shift;
192
my %opts = @_;
193
194
return undef unless $opts{table};
195
196
my $ret_value = [];
197
my $request = "SELECT ";
198
if ( exists $opts{count} && $opts{count} ) {
199
$request .= 'COUNT(*) AS cid ';
200
} else {
201
$request .= ( exists $opts{fields} && ref $opts{fields} eq 'ARRAY' ? join(', ',@{$opts{fields}}) : '*' );
202
}
203
my (@wheres, @values);
204
$request .= " FROM $opts{table}";
205
if ( exists $opts{id} && $opts{id} ) {
206
if (ref $opts{id} eq 'ARRAY') {
207
@wheres = ( "id IN (".join(',', map { '?' } @{$opts{id}} ).")" );
208
@values = @{ $opts{id} };
209
} else {
210
@wheres = ( "id = ?" );
211
@values = ( $opts{id} );
212
}
213
}
214
215
if ( exists $opts{wheres} && $opts{wheres} ) {
216
if ( ref $opts{wheres} eq 'ARRAY' ) {
217
push @wheres, @{ $opts{wheres} };
218
if ( exists $opts{values} && $opts{values} ) {
219
if ( exists $opts{values} && ref $opts{values} eq 'ARRAY' && @{ $opts{values} } ) {
220
push @values, @{ $opts{values} };
221
} elsif ( !ref $opts{values} ) {
222
push @values, $opts{values};
223
}
224
}
225
} elsif ( ref $opts{wheres} eq 'HASH' ) {
226
while ( my ($field, $value) = each %{ $opts{wheres} } ) {
227
push @wheres, "$field = ?";
228
push @values, $value;
229
}
230
} else {
231
push @wheres, $opts{wheres};
232
if ( exists $opts{values} && $opts{values} ) {
233
if ( exists $opts{values} && ref $opts{values} eq 'ARRAY' && @{ $opts{values} } ) {
234
push @values, @{ $opts{values} };
235
} elsif ( !ref $opts{values} ) {
236
push @values, $opts{values};
237
}
238
}
239
}
240
}
241
if ( @wheres ) {
242
$request .= ' WHERE '.join( ' and ', map { "($_)" } @wheres );
243
}
244
245
if ( $opts{order_by} ) {
246
$request .= " ORDER BY $opts{order_by}";
247
}
248
if ( $opts{limit} ) {
249
$request .= " LIMIT $opts{limit}";
250
}
251
if ( $opts{offset} ) {
252
$request .= " OFFSET $opts{offset}";
253
}
254
warn $request if $DEBUG;
255
warn "Values: [".join(',', map { defined $_ ? $_ : 'NULL' } @values )."]\n" if $DEBUG && @values;
256
my $dbh = $self->SQL;
257
my $result = $dbh->prepare($request);
258
my $res = $result->execute( @values );
259
260
unless ( $res ) {
261
warn $dbh->errstr;
262
warn "QUERY: [$request]\n";
263
warn "OPTS: [".Dumper(\%opts)."]\n";
264
return undef;
265
}
266
267
if ( $opts{count} ) {
268
my $ln = $result->fetchrow_hashref();
269
return $ln->{cid};
270
}
271
while ( my $ln = $result->fetchrow_hashref() ) {
272
push @$ret_value, $ln;
273
}
274
if ( $opts{id} && ref $opts{id} ne 'ARRAY' && ref $ret_value eq 'ARRAY' && scalar @$ret_value) {
275
$ret_value = $ret_value->[0];
276
} elsif ( $opts{id} && ref $opts{id} eq 'ARRAY' && ref $ret_value eq 'ARRAY' && scalar @$ret_value) {
277
278
} elsif ( $opts{id} ) {
279
return undef;
280
} elsif ( ref $ret_value ne 'ARRAY' ) {
281
return undef;
282
}
283
$result->finish;
284
return $ret_value;
285
}
286
287
288
# ---------------------------------------------------------------------------
289
# Получить следующий ID в объекте
290
# >> get_next_id
291
# table => название таблицы
292
#
293
# ----------------------------------------------------------------------------
294
sub get_next_id {
295
296
my $self = shift;
297
my $tablename = shift;
298
return unless $tablename;
299
300
my $request = "SELECT MAX(id)+1 FROM $tablename";
301
my $dbh = $self->SQL;
302
my $result = $dbh->prepare($request);
303
$result->execute();
304
my $ln = $result->fetchrow();
305
$result->finish;
306
307
return $ln;
308
}
309
310
311
# ---------------------------------------------------------------------------
312
# Сохранить Объект:
313
# store_object( 'tablename', key => 'value', key => 'value' )
314
# object => {
315
# key => 'value',
316
# }
317
# ----------------------------------------------------------------------------
318
sub store_object {
319
320
my $self = shift;
321
my $tablename = shift;
322
return unless $tablename;
323
my %object = @_;
324
325
my $request;
326
my @values;
327
my $id = 0;
328
if ( exists $object{id} && $object{id} ) {
329
$id = $object{id};
330
$request = "UPDATE $tablename SET " ;
331
my $i = 0;
332
foreach my $field ( keys %object ) {
333
next if $field eq 'id';
334
$request .= ',' if $i++;
335
$request .= " $field = ?";
336
push @values, $object{$field};
337
}
338
$request .= ' WHERE id='.$object{id};
339
} else {
340
$id = $self->get_next_id($tablename);
341
$request = "INSERT INTO $tablename " ;
342
my @keys = grep { $_ ne 'id' } keys %object;
343
@values = map { $object{$_} } @keys;
344
unshift @keys, 'id';
345
unshift @values, $id;
346
$request .= " (".join(', ', @keys).") ";
347
$request .= " VALUES (".join(', ', map { '?' } @keys).")";
348
}
349
warn $request."\n" if $DEBUG;
350
warn "Values: [".join(',',@values)."]\n" if $DEBUG;
351
352
my $dbh = $self->SQL;
353
my $result = $dbh->prepare($request);
354
my $res = $result->execute(@values);
355
unless ( $res ) {
356
warn "OBJECT STORE ERROR: [".$result->errstr."]\n";
357
}
358
$result->finish;
359
return $res ? $id : undef;
360
361
}
362
363
364
# ---------------------------------------------------------------------------
365
# Сохранить Объект с автоинкрементом:
366
#
367
# store_object_autoinc( 'tablename', key => 'value', key => 'value' )
368
# object => {
369
# key => 'value',
370
# }
371
#
372
# ----------------------------------------------------------------------------
373
sub store_object_autoinc {
374
375
my $self = shift;
376
my $tablename = shift;
377
return unless $tablename;
378
my (%object) = @_;
379
380
my $request;
381
my @values;
382
my $id = 0;
383
if ( exists $object{id} && $object{id} ) {
384
$id = $object{id};
385
$request = "UPDATE $tablename SET " ;
386
my $i = 0;
387
foreach my $field ( keys %object ) {
388
next if $field eq 'id';
389
$request .= ',' if $i++;
390
$request .= " $field = ?";
391
push @values, $object{$field};
392
}
393
$request .= ' WHERE id='.$object{id};
394
} else {
395
$request = "INSERT INTO $tablename " ;
396
my @keys = grep { $_ ne 'id' } keys %object;
397
@values = map { $object{$_} } @keys;
398
$request .= " (".join(', ', @keys).") ";
399
$request .= " VALUES (".join(', ', map { '?' } @keys).")";
400
}
401
warn $request."\n" if $DEBUG;
402
warn "Values: [".join(',', map { !defined $_ ? 'NULL' : $_ } @values)."]\n" if $DEBUG;
403
404
my $dbh = $self->SQL;
405
my $result = $dbh->prepare($request);
406
my $res = $result->execute(@values);
407
unless ( $res ) {
408
warn "OBJECT STORE ERROR: [".$result->errstr."]\n";
409
}
410
unless ( $id ) {
411
$id = $dbh->last_insert_id(undef, undef, undef, undef);
412
warn "INSERT TRACK. ID = [$id]\n";
413
}
414
$result->finish;
415
return $res ? $id : undef;
416
417
}
418
419
420
# ---------------------------------------------------------------------------
421
# Удалить Объект:
422
#
423
# ----------------------------------------------------------------------------
424
sub delete_object {
425
426
my $self = shift;
427
my $tablename = shift;
428
return unless $tablename;
429
my %opts = @_;
430
431
my $request;
432
if ( exists $opts{id} && $opts{id} ) {
433
$request = "DELETE FROM $tablename WHERE id = ?";
434
}
435
warn $request if $DEBUG;
436
437
my $dbh = $self->SQL;
438
my $result = $dbh->prepare($request);
439
$result->execute($opts{id});
440
$result->finish;
441
}
442
443
444
1;
utf8/plugins/MySQL/lib/MySQL/State.pm.proto
1
package MySQL::State;
2
3
use strict;
4
use warnings 'all';
5
use vars qw($AUTOLOAD);
6
use Contenido::Globals;
7
use Contenido::State;
8
use MySQL::Globals;
9
10
11
sub new {
12
my ($proto) = @_;
13
my $class = ref($proto) || $proto;
14
my $self = {};
15
bless $self, $class;
16
17
# configured
18
$self->{debug} = (lc('') eq 'yes');
19
$self->{project} = '';
20
21
# зашитая конфигурация плагина
22
$self->{db_type} = 'none'; ### For REAL database use 'remote'
23
$self->{db_keepalive} = 0;
24
$self->{db_host} = '@MYSQL_DB_HOST@';
25
$self->{db_name} = '@MYSQL_DB_NAME@';
26
$self->{db_user} = '@MYSQL_DB_USER@';
27
$self->{db_password} = '@MYSQL_DB_PASSWORD@';
28
$self->{db_port} = '@MYSQL_DB_PORT@';
29
$self->{db_client_encoding} = '@MYSQL_DB_ENCODING@' || 'utf8';
30
$self->{store_method} = 'toast';
31
$self->{cascade} = 1;
32
$self->{db_prepare} = 0;
33
34
$self->{memcached_enable} = lc( '' ) eq 'yes' ? 1 : 0;
35
$self->{memcached_enable_compress} = 1;
36
$self->{memcached_backend} = '';
37
$self->{memcached_servers} = [qw()];
38
$self->{memcached_busy_lock} = 60;
39
$self->{memcached_delayed} = lc('') eq 'yes' ? 1 : 0;
40
41
$self->{serialize_with} = 'json'; ### or 'dumper'
42
43
# not implemented really (core compatibility)
44
$self->{binary_directory} = '/nonexistent';
45
$self->{data_directory} = '/nonexistent';
46
$self->{images_directory} = '/nonexistent';
47
$self->{preview} = '0';
48
49
$self->_init_();
50
$self;
51
}
52
53
sub info {
54
my $self = shift;
55
return unless ref $self;
56
57
for (sort keys %{$self->{attributes}}) {
58
my $la = length $_;
59
warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n";
60
}
61
}
62
63
sub _init_ {
64
my $self = shift;
65
66
# зашитая конфигурация плагина
67
$self->{attributes}->{$_} = 'SCALAR' for qw(
68
debug
69
project
70
71
db_type
72
db_keepalive
73
db_host
74
db_port
75
db_name
76
db_user
77
db_password
78
store_method
79
cascade
80
db_prepare
81
db_client_encoding
82
83
memcached_enable
84
memcached_enable_compress
85
memcached_backend
86
memcached_servers
87
memcached_busy_lock
88
memcached_delayed
89
90
binary_directory
91
data_directory
92
images_directory
93
preview
94
);
95
}
96
97
sub AUTOLOAD {
98
my $self = shift;
99
my $attribute = $AUTOLOAD;
100
101
$attribute =~ s/.*:://;
102
return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY
103
104
if (!exists $self->{attributes}->{$attribute}) {
105
warn "Contenido Error (MySQL::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n";
106
return;
107
}
108
109
$self->{$attribute} = shift @_ if $#_>=0;
110
$self->{$attribute};
111
}
112
113
1;
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.