Revision 599
Date:
2016/10/19 16:17:37
Author:
ahitrov
Revision Log:
Initial plugin
Files:
Legend:
Added
Removed
Modified
utf8/plugins/sphinx/comps/contenido/sphinx/autohandler
1
<%init>
2
3
$r->content_type('text/html');
4
$m->call_next();
5
6
</%init>
utf8/plugins/sphinx/comps/contenido/sphinx/dhandler
1
<& $call, %ARGS &>
2
<%init>
3
4
my $call;
5
if ( $r->uri eq '/contenido/sphinx/' ) {
6
$call = 'index.html';
7
} else {
8
&abort404;
9
}
10
11
</%init>
utf8/plugins/sphinx/comps/contenido/sphinx/index.html
1
<& "/contenido/components/header.msn" &>
2
<& "/contenido/components/naviline.msn" &>
3
4
<p>PLugin [sphinx]</p>
5
6
</body>
7
</html>
utf8/plugins/sphinx/config.proto
1
#############################################################################
2
#
3
# Параметры данного шаблона необходимо ВРУЧНУЮ добавить в config.mk проекта
4
# и привести в соответствие с требованиями проекта
5
#
6
#############################################################################
7
8
PLUGINS += sphinx
9
10
SPHINX_HOST = localhost
11
SPHINX_PORT = 9306
12
13
REWRITE += SPHINX_HOST SPHINX_PORT
utf8/plugins/sphinx/lib/sphinx/Apache.pm
1
package sphinx::Apache;
2
3
use strict;
4
use warnings 'all';
5
6
use sphinx::State;
7
use Contenido::Globals;
8
9
10
sub child_init {
11
# встраиваем keeper плагина в keeper проекта
12
$keeper->{sphinx} = sphinx::Keeper->new($state->sphinx);
13
}
14
15
sub request_init {
16
}
17
18
sub child_exit {
19
}
20
21
1;
utf8/plugins/sphinx/lib/sphinx/Init.pm
1
package sphinx::Init;
2
3
use strict;
4
use warnings 'all';
5
6
use Contenido::Globals;
7
use sphinx::Apache;
8
use sphinx::Keeper;
9
10
11
# загрузка всех необходимых плагину классов
12
# sphinx::SQL::SomeTable
13
# sphinx::SomeClass
14
Contenido::Init::load_classes(qw(
15
));
16
17
sub init {
18
0;
19
}
20
21
1;
utf8/plugins/sphinx/lib/sphinx/Keeper.pm
1
package sphinx::Keeper;
2
3
use strict;
4
use warnings 'all';
5
use base qw(Contenido::Keeper);
6
use Contenido::Globals;
7
8
######################
9
# Отправить объект в поиск:
10
# Вызов: $keeper->set_search( $object );
11
# Объект должен обязательно иметь метод
12
# ->get_search_data,
13
# возвращающий структуру:
14
# {
15
# id => $object->id,
16
# class => $object->class,
17
# name => $object_name,
18
# text => $search_text
19
# }
20
# Кроме того, метод чувствителен к полю status и предполагает,
21
# что status=1 - документ активен; status=0 - документ не активен.
22
##########################################################################
23
sub send
24
{
25
my $self = shift;
26
my $doc = shift;
27
return undef unless ref $doc && $doc->id;
28
29
my ($object) = $self->get_documents(
30
class => 'sphinx::Search',
31
object_id => $doc->id,
32
object_class => $doc->class,
33
);
34
if ( $doc->status == 1 ) {
35
my $data = $doc->get_search_data;
36
return undef unless $data;
37
unless ( ref $object ) {
38
$object = sphinx::Search->new( $self );
39
$object->status( 1 );
40
$object->is_deleted( 0 );
41
$object->object_id( $doc->id );
42
$object->object_class( $doc->class );
43
$object->name( $data->{name} );
44
$object->search( $data->{text} );
45
$object->store;
46
} else {
47
if ( $data->{name} ne $object->name || $data->{text} ne $object->search || $doc->is_deleted || $doc->status <= 0 ) {
48
$object->status( 1 );
49
$object->is_deleted( 0 );
50
$object->name( $data->{name} );
51
$object->search( $data->{text} );
52
$object->store;
53
}
54
}
55
} else {
56
if ( ref $object ) {
57
$object->status( 0 );
58
$object->is_deleted( 1 );
59
$object->store;
60
}
61
}
62
}
63
64
65
1;
utf8/plugins/sphinx/lib/sphinx/Search.pm
1
package sphinx::Search;
2
3
use base 'Contenido::Document';
4
use Contenido::Globals;
5
6
sub extra_properties
7
{
8
return (
9
)
10
}
11
12
13
sub sections
14
{
15
return ();
16
}
17
18
sub class_name
19
{
20
return 'Sphinx: поисковый объект';
21
}
22
23
sub class_description
24
{
25
return 'Sphinx: поисковый объект';
26
}
27
28
sub class_table
29
{
30
return 'sphinx::SQL::SearchTable';
31
}
32
33
sub search_fields {
34
return ('name', 'object_id');
35
}
36
37
sub pre_store
38
{
39
my $self = shift;
40
41
return 1;
42
}
43
44
45
1;
utf8/plugins/sphinx/lib/sphinx/SQL/SearchTable.pm
1
package sphinx::SQL::SearchTable;
2
3
use base 'SQL::ProtoTable';
4
5
sub db_table
6
{
7
return 'search';
8
}
9
10
sub db_id_sequence {
11
return 'search_id_seq';
12
}
13
14
sub available_filters {
15
my @available_filters = qw(
16
_class_filter
17
_status_filter
18
_in_id_filter
19
_id_filter
20
_name_filter
21
_class_excludes_filter
22
23
_object_id_filter
24
_object_class_filter
25
_excludes_filter
26
);
27
return \@available_filters;
28
}
29
30
# ----------------------------------------------------------------------------
31
# Свойства храним в массивах, потому что порядок важен!
32
# Это общие свойства - одинаковые для всех документов.
33
#
34
# attr - обязательный параметр, название атрибута;
35
# type - тип аттрибута, требуется для отображдения;
36
# rusname - русское название, опять же требуется для отображения;
37
# hidden - равен 1, когда
38
# readonly - инициализации при записи только без изменения в дальнейшем
39
# db_field - поле в таблице
40
# default - значение по умолчанию (поле всегда имеет это значение)
41
# ----------------------------------------------------------------------------
42
sub required_properties
43
{
44
return (
45
{ # Идентификатор документа, сквозной по всем типам...
46
'attr' => 'id',
47
'type' => 'integer',
48
'rusname' => 'Идентификатор документа',
49
'hidden' => 1,
50
'auto' => 1,
51
'readonly' => 1,
52
'db_field' => 'id',
53
'db_type' => 'integer',
54
'db_opts' => "not null default nextval('public.documents_id_seq'::text)",
55
},
56
{ # Класс документа...
57
'attr' => 'class',
58
'type' => 'string',
59
'rusname' => 'Класс документа',
60
'hidden' => 1,
61
'readonly' => 1,
62
'column' => 3,
63
'db_field' => 'class',
64
'db_type' => 'varchar(48)',
65
'db_opts' => 'not null',
66
},
67
{
68
'attr' => 'name',
69
'type' => 'string',
70
'rusname' => 'Название документа',
71
'column' => 2,
72
'db_field' => 'name',
73
'db_type' => 'text',
74
},
75
{
76
'attr' => 'status',
77
'type' => 'status',
78
'rusname' => 'Статус',
79
'db_field' => 'status',
80
'db_type' => 'smallint',
81
},
82
{
83
'attr' => 'is_deleted',
84
'type' => 'checkbox',
85
'rusname' => 'Документ удален',
86
'db_field' => 'is_deleted',
87
'db_type' => 'boolean',
88
},
89
{ # Время создания документа
90
'attr' => 'ctime',
91
'type' => 'datetime',
92
'rusname' => 'Дата/время создания',
93
'readonly' => 1,
94
'auto' => 1,
95
'hidden' => 1,
96
'db_field' => 'ctime',
97
'db_type' => 'timestamp',
98
'db_opts' => 'not null default now()',
99
'default' => 'CURRENT_TIMESTAMP',
100
},
101
{ # Время изменения документа
102
'attr' => 'mtime',
103
'type' => 'datetime',
104
'rusname' => 'Дата/время изменения',
105
'auto' => 1,
106
'hidden' => 1,
107
'db_field' => 'mtime',
108
'db_type' => 'timestamp',
109
'db_opts' => 'not null default now()',
110
'default' => 'CURRENT_TIMESTAMP',
111
},
112
{
113
'attr' => 'object_class',
114
'type' => 'string',
115
'rusname' => 'Класс объекта',
116
'column' => 4,
117
'db_field' => 'object_class',
118
'db_type' => 'text',
119
},
120
{
121
'attr' => 'object_id',
122
'type' => 'string',
123
'rusname' => 'ID объекта',
124
'column' => 5,
125
'db_field' => 'object_id',
126
'db_type' => 'integer',
127
},
128
{
129
'attr' => 'search',
130
'type' => 'text',
131
'rusname' => 'Текст',
132
'db_field' => 'search',
133
'db_type' => 'text',
134
},
135
);
136
}
137
138
139
########### FILTERS DESCRIPTION ####################################################################################
140
sub _excludes_filter {
141
my ($self,%opts)=@_;
142
if (exists $opts{excludes}) {
143
# - исключение из отбора
144
my @eids = ();
145
if (ref($opts{excludes}) eq 'ARRAY') {
146
@eids = @{ $opts{excludes} };
147
} elsif ($opts{excludes} =~ /[^\d\,]/) {
148
warn "Contenido Warning: В списке идентификаторов для исключения встречаются нечисловые элементы. Параметр excludes игнорируется (".$opts{excludes}.").\n";
149
} else {
150
@eids = split(',', $opts{excludes});
151
}
152
153
my $excludes = join(',', @eids);
154
155
# Меняется логика запроса, если это join-запрос.
156
# Потому что в этом случае гораздо лучше ограничить выборку по таблице links,
157
# чем производить полную склейку таблиц.
158
if (@eids) {
159
if (exists($opts{ldest}) || exists($opts{lsource})) {
160
if (exists($opts{ldest})) {
161
return " (l.source_id not in ($excludes)) ";
162
} elsif (exists($opts{lsource})) {
163
return " (l.dest_id not in ($excludes)) ";
164
}
165
} else {
166
return " (d.id not in ($excludes)) ";
167
}
168
}
169
}
170
return undef;
171
}
172
173
174
sub _get_orders {
175
my ($self, %opts) = @_;
176
177
if ($opts{order_by}) {
178
return ' order by '.$opts{order_by};
179
} else {
180
return ' order by mtime desc';
181
}
182
return undef;
183
}
184
185
sub _object_id_filter {
186
my ($self, %opts)=@_;
187
return undef unless ( exists $opts{object_id} );
188
return &SQL::Common::_generic_int_filter('d.object_id', $opts{object_id});
189
}
190
191
sub _object_class_filter {
192
my ($self, %opts)=@_;
193
return undef unless ( exists $opts{object_class} );
194
return &SQL::Common::_generic_text_filter('d.object_class', $opts{object_class});
195
}
196
197
1;
utf8/plugins/sphinx/lib/sphinx/State.pm.proto
1
package sphinx::State;
2
3
use strict;
4
use warnings 'all';
5
use vars qw($AUTOLOAD);
6
7
8
sub new {
9
my ($proto) = @_;
10
my $class = ref($proto) || $proto;
11
my $self = {};
12
bless $self, $class;
13
14
# configured
15
$self->{debug} = (lc('') eq 'yes');
16
$self->{project} = '';
17
$self->{contenido_notab} = 0;
18
$self->{tab_name} = 'sphinx';
19
20
# зашитая конфигурация плагина
21
$self->{db_type} = 'none'; ### For REAL database use 'remote'
22
$self->{db_keepalive} = 0;
23
$self->{db_host} = '';
24
$self->{db_name} = '';
25
$self->{db_user} = '';
26
$self->{db_password} = '';
27
$self->{db_port} = '';
28
$self->{store_method} = 'toast';
29
$self->{cascade} = 1;
30
$self->{db_prepare} = 0;
31
32
$self->{memcached_enable} = lc( '' ) eq 'yes' ? 1 : 0;
33
$self->{memcached_enable_compress} = 1;
34
$self->{memcached_backend} = '';
35
$self->{memcached_servers} = [qw()];
36
$self->{memcached_busy_lock} = 60;
37
$self->{memcached_delayed} = lc('') eq 'yes' ? 1 : 0;
38
39
$self->{serialize_with} = 'json'; ### or 'dumper'
40
41
# not implemented really (core compatibility)
42
$self->{binary_directory} = '/nonexistent';
43
$self->{data_directory} = '/nonexistent';
44
$self->{images_directory} = '/nonexistent';
45
$self->{preview} = '0';
46
47
$self->_init_();
48
$self;
49
}
50
51
sub info {
52
my $self = shift;
53
return unless ref $self;
54
55
for (sort keys %{$self->{attributes}}) {
56
my $la = length $_;
57
warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n";
58
}
59
}
60
61
sub _init_ {
62
my $self = shift;
63
64
# зашитая конфигурация плагина
65
$self->{attributes}->{$_} = 'SCALAR' for qw(
66
debug
67
project
68
tab_name
69
70
db_type
71
db_keepalive
72
db_host
73
db_port
74
db_name
75
db_user
76
db_password
77
store_method
78
cascade
79
db_prepare
80
db_client_encoding
81
82
memcached_enable
83
memcached_enable_compress
84
memcached_backend
85
memcached_servers
86
memcached_busy_lock
87
memcached_delayed
88
89
binary_directory
90
data_directory
91
images_directory
92
preview
93
);
94
}
95
96
sub AUTOLOAD {
97
my $self = shift;
98
my $attribute = $AUTOLOAD;
99
100
$attribute =~ s/.*:://;
101
return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY
102
103
if (!exists $self->{attributes}->{$attribute}) {
104
warn "Contenido Error (sphinx::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n";
105
return;
106
}
107
108
$self->{$attribute} = shift @_ if $#_>=0;
109
$self->{$attribute};
110
}
111
112
1;
utf8/plugins/sphinx/sql/TOAST/search.sql
1
create sequence search_id_seq;
2
select setval('search_id_seq', 1, true);
3
4
create table search
5
(
6
id integer not null primary key default nextval('public.search_id_seq'::text),
7
class text not null,
8
ctime timestamp not null default now(),
9
mtime timestamp not null default now(),
10
status smallint not null default 1,
11
object_id integer not null,
12
object_class text not null,
13
name text,
14
search text,
15
data text
16
);
17
create index search_object on search (object_class, object_id);
18
create index search_mtime on search (mtime);
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.