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 – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.