Revision 234
Date:
2012/08/28 09:26:59
Author:
ahitrov
Revision Log:
Blog plugin
Files:
Legend:
Added
Removed
Modified
utf8/plugins/blogs/comps/contenido/blogs/autohandler
1
<%init>
2
3
$r->content_type('text/html');
4
$m->call_next();
5
6
</%init>
utf8/plugins/blogs/comps/contenido/blogs/dhandler
1
<& $call, %ARGS &>
2
<%init>
3
4
my $call;
5
if ( $r->uri eq '/contenido/blogs/' ) {
6
$call = 'index.html';
7
} else {
8
&abort404;
9
}
10
11
</%init>
utf8/plugins/blogs/comps/contenido/blogs/index.html
1
<& "/contenido/components/header.msn" &>
2
<& "/contenido/components/naviline.msn" &>
3
4
<p>PLugin [blogs]</p>
5
6
</body>
7
</html>
utf8/plugins/blogs/lib/blogs/Apache.pm
1
package blogs::Apache;
2
3
use strict;
4
use warnings 'all';
5
6
use blogs::State;
7
use Contenido::Globals;
8
9
10
sub child_init {
11
# встраиваем keeper плагина в keeper проекта
12
$keeper->{blogs} = blogs::Keeper->new($state->blogs);
13
}
14
15
sub request_init {
16
}
17
18
sub child_exit {
19
}
20
21
1;
utf8/plugins/blogs/lib/blogs/Blog.pm
1
package blogs::Blog;
2
3
use base "Contenido::Document";
4
use Contenido::Globals;
5
6
sub extra_properties
7
{
8
return (
9
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус чтения/комментирования',
10
'cases' => [
11
[0, 'Заблокирован'],
12
[1, 'Открыт для комментирования всеми'],
13
[2, 'Открыт для комментирования членами сообщества'],
14
[3, 'Открыт только для чтения'],
15
],
16
},
17
{ 'attr' => 'type', 'type' => 'status', 'rusname' => 'Статус записи',
18
'cases' => [
19
[0, 'Личный блог'],
20
[1, 'Блог сообщества'],
21
[2, 'Публичный блог'],
22
[3, 'Системный блог (contenido)'],
23
],
24
},
25
{ 'attr' => 'abstr', 'type' => 'wysiwyg', 'rusname' => 'Краткое описание', 'rows' => 10 },
26
{ 'attr' => 'icon', 'type' => 'image', 'rusname' => 'Аватар', preview => ['300x300','250x250','200x200','150x150','120x120','100x100','32x32'] },
27
)
28
}
29
30
sub class_name
31
{
32
return 'Блог';
33
}
34
35
sub class_description
36
{
37
return 'Профиль блога';
38
}
39
40
sub class_table
41
{
42
return 'blogs::SQL::BlogsTable';
43
}
44
45
sub can_read {
46
my $self = shift;
47
my $uid = shift;
48
return undef unless $uid;
49
50
if ( $uid == $self->uid || $self->status == 1 || ($self->status == 2 && $self->is_member($uid)) ) {
51
return 1;
52
} else {
53
return 0;
54
}
55
}
56
57
sub why_cant_i_read {
58
my $self = shift;
59
my $uid = shift;
60
return 'не указан пользователь' unless $uid;
61
62
if ( $self->status == 0 ) {
63
return 'блог заблокирован';
64
} elsif ( $self->status == 2 && !$self->is_member($uid) ) {
65
return 'блог открыт только для участников сообщества';
66
} else {
67
return 'окстись, ты можешь прекрасно все читать...';
68
}
69
}
70
71
sub can_write {
72
my $self = shift;
73
my $uid = shift;
74
return undef unless $uid;
75
76
return 1 if $uid == $self->uid;
77
return 0 unless $self->status;
78
if ( $self->type == 2 || ($self->type == 1 && $self->is_member($uid)) || ($self->type == 3 && ref $user) ) {
79
return 1;
80
} else {
81
return 0;
82
}
83
}
84
85
sub why_cant_i_write {
86
my $self = shift;
87
my $uid = shift;
88
return 'не указан пользователь' unless $uid;
89
90
if ( $self->status == 0 ) {
91
return 'блог заблокирован';
92
} elsif ( $self->type == 0 && $self->uid != $uid ) {
93
return 'это чужой личный блог';
94
} elsif ( $self->type == 1 && !$self->is_member($uid) ) {
95
return 'вы не являетесь участником данного сообщества';
96
} else {
97
return 'окстись, ты можешь прекрасно все читать...';
98
}
99
}
100
101
sub is_member {
102
my $self = shift;
103
my $uid = shift;
104
return undef unless $uid;
105
106
return (($uid == $self->uid) || (grep { $_ == $uid } $self->members)) ? 1 : 0;
107
}
108
109
sub is_moderator {
110
my $self = shift;
111
my $uid = shift;
112
return undef unless $uid;
113
114
return $uid == $self->uid || grep { $_ == $uid } $self->moderators ? 1 : 0;
115
}
116
117
sub is_subscriber {
118
my $self = shift;
119
my $uid = shift;
120
return undef unless $uid;
121
122
return (($uid == $self->uid) || (grep { $_ == $uid } $self->members, $self->readers)) ? 1 : 0;
123
}
124
125
sub subscribe {
126
my $self = shift;
127
my $uid = shift;
128
return undef unless $uid;
129
return undef unless $self->id;
130
return undef if $self->is_subscriber($uid);
131
132
my $link = blogs::SubscriberLink->new ( $keeper );
133
$link->status( 1 );
134
$link->source_id( $uid );
135
$link->dest_id( $self->id );
136
$link->dest_class( $self->class );
137
$link->store;
138
return undef unless $link->id;
139
140
push @{ $self->{readers} }, $uid;
141
$self->store;
142
return 1;
143
}
144
145
sub unsubscribe {
146
my $self = shift;
147
my $uid = shift;
148
return undef unless $uid;
149
return undef unless $self->id;
150
151
warn "DELETE FROM ".blogs::SubscriberLink->class_table->db_table." WHERE source_id = ? AND dest_id = ? AND dest_class = ?\n" if $DEBUG;
152
my $res = $keeper->SQL->prepare('DELETE FROM '.blogs::SubscriberLink->class_table->db_table.' WHERE source_id = ? AND dest_id = ? AND dest_class = ?');
153
$res->execute( $uid, $self->id, $self->class );
154
$res->finish;
155
156
@{ $self->{readers} } = grep { $_ != $uid } @{ $self->{readers} };
157
$self->store;
158
return 1;
159
}
160
161
sub contenido_status_style
162
{
163
my $self = shift;
164
if ( $self->status == 2 ) {
165
return 'color:green;';
166
} elsif ( $self->status == 3 ) {
167
return 'color:olive;';
168
} elsif ( $self->status == 4 ) {
169
return 'color:green;';
170
} elsif ( $self->status == 5 ) {
171
return 'color:red;';
172
}
173
}
174
175
sub search_fields
176
{
177
return ('name');
178
}
179
180
sub table_links
181
{
182
return [
183
{ name => 'Записи', class => 'blogs::Record', filter => 'blog_id', field => 'blog_id' },
184
];
185
}
186
187
sub pre_store
188
{
189
my $self = shift;
190
191
unless ( $self->id ) {
192
if ( $self->status == 3 && ref $user ) {
193
$self->uid( $user->id );
194
}
195
}
196
197
my $default_section = $project->s_alias->{blogs} if ref $project->s_alias eq 'HASH' && exists $project->s_alias->{blogs};
198
if ( $default_section ) {
199
my $sections = $self->{sections};
200
if ( ref $sections eq 'ARRAY' && scalar @$sections ) {
201
my @new_sects = grep { $_ != $default_section } @$sections;
202
push @new_sects, $default_section;
203
$self->sections(@new_sects);
204
} elsif ( $sections && !ref $sections && $sections != $default_section ) {
205
my @new_sects = ($default_section, $sections);
206
$self->sections(@new_sects);
207
} else {
208
$self->sections($default_section);
209
}
210
}
211
212
return 1;
213
}
214
215
1;
utf8/plugins/blogs/lib/blogs/BlogSection.pm
1
package blogs::BlogSection;
2
3
use base 'Contenido::Section';
4
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'filters', 'hidden' => 1 },
9
{ 'attr' => 'default_document_class', 'default' => 'blogs::Blog' },
10
# { 'attr' => 'brief', 'type' => 'text', 'rusname' => 'Описание секции' },
11
)
12
}
13
14
sub class_name
15
{
16
return 'Секция блогов';
17
}
18
19
sub class_description
20
{
21
return 'Секция блогов';
22
}
23
24
1;
utf8/plugins/blogs/lib/blogs/Init.pm
1
package blogs::Init;
2
3
use strict;
4
use warnings 'all';
5
6
use Contenido::Globals;
7
use blogs::Apache;
8
use blogs::Keeper;
9
10
11
# загрузка всех необходимых плагину классов
12
# blogs::SQL::SomeTable
13
# blogs::SomeClass
14
Contenido::Init::load_classes(qw(
15
blogs::SQL::BlogsTable
16
blogs::SQL::RecordsTable
17
blogs::SQL::MembersTable
18
blogs::SQL::CommentsTable
19
blogs::SQL::TagsTable
20
blogs::SQL::TagCloudTable
21
22
blogs::TagCloud
23
24
blogs::Blog
25
blogs::Record
26
blogs::Comment
27
blogs::MemberLink
28
blogs::Tag
29
blogs::SubscriberLink
30
31
blogs::RecordSection
32
blogs::TagSection
33
blogs::BlogSection
34
));
35
36
sub init {
37
push @{ $state->{'available_documents'} }, qw( blogs::Blog blogs::Record blogs::Comment blogs::Tag );
38
push @{ $state->{'available_sections'} }, qw( blogs::RecordSection blogs::TagSection blogs::BlogSection );
39
push @{ $state->{'available_links'} }, qw( blogs::MemberLink blogs::TagCloud );
40
0;
41
}
42
43
1;
utf8/plugins/blogs/lib/blogs/Keeper.pm
1
package blogs::Keeper;
2
3
use strict;
4
use warnings 'all';
5
use base qw(Contenido::Keeper);
6
7
8
use Contenido::Globals;
9
10
11
sub get_blogs {
12
my $self = shift;
13
my (%opts) = @_;
14
my $uid = delete $opts{uid} || 0;
15
my $owner = delete $opts{owner} || 0;
16
my $type = delete $opts{type};
17
my $id = delete $opts{id};
18
19
my $blog;
20
my %getopts;
21
if ( $uid ) {
22
$getopts{status} = 'positive';
23
} else {
24
$getopts{status} = [1,2,3];
25
}
26
$getopts{uid} = $owner if $owner;
27
$getopts{type} = defined $type ? $type : 'positive';
28
if ( $id ) {
29
$blog = $keeper->get_document_by_id ( $id, class => 'blogs::Blog', %getopts );
30
if ( ref $blog ) {
31
if ( $blog->type == 1 && $uid && $blog->uid != $uid ) {
32
return undef unless grep { $_ == $uid } $blog->members;
33
}
34
}
35
} else {
36
if ( $uid ) {
37
$getopts{lclass} = 'blogs::MemberLink',
38
$getopts{lsource} = $uid;
39
}
40
$blog = $keeper->get_documents (
41
class => 'blogs::Blog',
42
%getopts,
43
%opts,
44
return_mode => 'array_ref',
45
);
46
if ( $owner && defined $type && $type == 0 ) {
47
$blog = $blog->[0];
48
}
49
}
50
return $blog;
51
}
52
53
sub add_member {
54
my $self = shift;
55
my (%opts) = @_;
56
my $id = delete $opts{blog} || 0;
57
my $uid = delete $opts{uid} || [];
58
my $moderator = delete $opts{moderator} || 0;
59
return undef if !$uid || (ref $uid eq 'ARRAY' && !@$uid);
60
return undef unless $id;
61
62
$uid = [$uid] unless ref $uid;
63
my $blog = $keeper->get_document_by_id ( $id, class => 'blogs::Blog' );
64
my @members = grep { my $mem = $_; !grep { $_ == $mem } @$uid } $blog->members;
65
push @members, @$uid;
66
$blog->members( @members ? @members : undef );
67
if ( $moderator ) {
68
my @moderators = grep { my $mem = $_; !grep { $_ == $mem } @$uid } $blog->moderators;
69
push @moderators, @$uid;
70
$blog->moderators( @moderators ? @moderators : undef );
71
}
72
$blog->store;
73
74
my @links = $keeper->get_links ( class => 'blogs::MemberLink', dest_id => $blog->id );
75
foreach my $user_id ( @$uid ) {
76
my ($link) = grep { $_->source_id == $user_id } @links;
77
if ( ref $link && $moderator ) {
78
$link->moderator( 1 );
79
$link->store;
80
} elsif ( ref $link ) {
81
$link->moderator( 0 );
82
$link->store;
83
} else {
84
$link = blogs::MemberLink->new( $keeper );
85
$link->source_id( $user_id );
86
$link->dest_id ( $blog->id );
87
$link->dest_class ( $blog->class );
88
$link->status( 1 );
89
$link->moderator( $moderator );
90
$link->store;
91
}
92
}
93
1;
94
}
95
96
sub remove_member {
97
my $self = shift;
98
my (%opts) = @_;
99
my $id = delete $opts{blog} || 0;
100
my $uid = delete $opts{uid} || 0;
101
return undef unless $uid;
102
return undef unless $id;
103
104
my $blog = $keeper->get_document_by_id ( $id, class => 'blogs::Blog' );
105
my @members = grep { $_ != $uid } $blog->members;
106
$blog->members( @members ? @members : undef );
107
my @moderators = grep { $_!= $uid } $blog->moderators;
108
$blog->moderators( @moderators ? @moderators : undef );
109
$blog->store;
110
111
my @links = $keeper->get_links ( class => 'blogs::MemberLink', dest_id => $blog->id );
112
my ($link) = grep { $_->source_id == $uid } @links;
113
if ( ref $link ) {
114
$link->delete;
115
}
116
1;
117
}
118
119
120
sub delete_blog {
121
my $self = shift;
122
my $id = shift;
123
my (%opts) = @_;
124
125
if ( $opts{soft} ) {
126
my $blog = $keeper->get_document_by_id ( $id, class => 'blogs::Blog' );
127
$blog->status(0);
128
$blog->store;
129
} else {
130
my $comments_db_table = blogs::SQL::CommentsTable->db_table;
131
my $sthcom = $keeper->SQL->prepare( "delete from $comments_db_table where blog_id = ?" );
132
$sthcom->execute( $id );
133
$sthcom->finish;
134
my $records = $keeper->get_documents (
135
blog_id => $id,
136
class => 'blogs::Record',
137
no_limit => 1,
138
return_mode => 'array_ref',
139
);
140
foreach my $rec ( @$records ) {
141
$rec->delete( attachments => 1 );
142
}
143
my $blog = $keeper->get_document_by_id ( $id, class => 'blogs::Blog' );
144
$blog->delete( attachments => 1 );
145
}
146
}
147
148
149
sub delete_record {
150
my $self = shift;
151
my $id = shift;
152
my (%opts) = @_;
153
154
my $comments_db_table = blogs::SQL::CommentsTable->db_table;
155
my $sthcom = $keeper->SQL->prepare( "delete from $comments_db_table where record_id = ?" );
156
$sthcom->execute( $id );
157
$sthcom->finish;
158
my $record = $keeper->get_document_by_id ( $id, class => 'blogs::Record' );
159
$record->delete( attachments => 1 );
160
}
161
162
sub add_comment {
163
my $self = shift;
164
my (%opts) = @_;
165
}
166
167
168
sub get_comments {
169
my $self = shift;
170
my (%opts) = @_;
171
my $record_id = delete $opts{record_id};
172
173
return undef unless $record_id;
174
my $comment_tree = {};
175
my $comments = $keeper->get_documents (
176
class => 'blogs::Comment',
177
record_id => $record_id,
178
return_mode => 'array_ref',
179
) || [];
180
foreach my $comment ( @$comments ) {
181
$comment_tree->{$comment->pid} = [] unless exists $comment_tree->{$comment->pid};
182
push @{ $comment_tree->{$comment->pid} }, $comment;
183
}
184
return ( $comment_tree, $comments );
185
}
186
187
1;
utf8/plugins/blogs/lib/blogs/MemberLink.pm
1
package blogs::MemberLink;
2
3
use strict;
4
use vars qw(@ISA);
5
6
use Contenido::Link;
7
@ISA = ('Contenido::Link');
8
9
sub extra_properties
10
{
11
return (
12
);
13
}
14
15
sub class_name
16
{
17
return 'Участник блога';
18
}
19
20
sub class_description
21
{
22
return 'Связь между пользователями и блогами';
23
}
24
25
#sub available_sources
26
#{
27
# return [ qw(formula1::Championship) ];
28
#}
29
30
sub available_destinations
31
{
32
return [ qw(blogs::Blog) ];
33
}
34
35
sub class_table
36
{
37
return 'blogs::SQL::MembersTable';
38
}
39
40
1;
utf8/plugins/blogs/lib/blogs/Record.pm
1
package blogs::Record;
2
3
use base "Contenido::Document";
4
use Encode;
5
use Contenido::Globals;
6
7
sub extra_properties
8
{
9
return (
10
{ 'attr' => 'name', 'type' => 'string', 'rusname' => 'Сабж / настроение' },
11
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус записи',
12
'cases' => [
13
[0, 'Запись неактивна'],
14
[1, 'Запись активна'],
15
[2, 'Запись вынесена в топ блога'],
16
[3, 'Запись вынесена на главную страницу'],
17
],
18
},
19
{ 'attr' => 'abstr', 'type' => 'wysiwyg', 'rusname' => 'Начало записи', 'rows' => 10 },
20
{ 'attr' => 'body', 'type' => 'wysiwyg', 'rusname' => 'Текст под катом', 'rows' => 30 },
21
{ 'attr' => 'author', 'type' => 'string', 'rusname' => 'Имя автора сообщения', },
22
{ 'attr' => 'video', 'type' => 'text', 'rusname' => 'Поле для вставки HTML-кода видео', 'rows' => 10 },
23
{ 'attr' => 'pictures', 'type' => 'images', 'rusname' => 'Список картинок', preview => ['300x300','250x250','200x200','150x150','120x120','100x100'] },
24
)
25
}
26
27
sub class_name
28
{
29
return 'Запись блога';
30
}
31
32
sub class_description
33
{
34
return 'Запись блога';
35
}
36
37
38
sub class_table
39
{
40
return 'blogs::SQL::RecordsTable';
41
}
42
43
sub contenido_status_style
44
{
45
my $self = shift;
46
if ( $self->status == 2 ) {
47
return 'color:green;';
48
} elsif ( $self->status == 3 ) {
49
return 'color:olive;';
50
} elsif ( $self->status == 4 ) {
51
return 'color:green;';
52
} elsif ( $self->status == 5 ) {
53
return 'color:red;';
54
}
55
}
56
57
sub search_fields
58
{
59
return ('tags', 'name');
60
}
61
62
sub table_links
63
{
64
return [
65
{ name => 'Комменты', class => 'blogs::Comment', filter => 'record_id', field => 'record_id' },
66
];
67
}
68
69
sub pre_store
70
{
71
my $self = shift;
72
73
if ( !$self->id && !$self->uid ) {
74
if ( ref $session && exists $session->{id} && $session->{id} ) {
75
$self->uid( $session->{id} );
76
} elsif ( ref $user ) {
77
$self->uid( $user->id );
78
}
79
}
80
81
my $default_section = $project->s_alias->{blog_records} if ref $project->s_alias eq 'HASH' && exists $project->s_alias->{blog_records};
82
if ( $default_section ) {
83
my $sections = $self->{sections};
84
if ( ref $sections eq 'ARRAY' && scalar @$sections ) {
85
my @new_sects = grep { $_ != $default_section } @$sections;
86
push @new_sects, $default_section;
87
$self->sections(@new_sects);
88
} elsif ( $sections && !ref $sections && $sections != $default_section ) {
89
my @new_sects = ($default_section, $sections);
90
$self->sections(@new_sects);
91
} else {
92
$self->sections($default_section);
93
}
94
}
95
96
return 1;
97
}
98
99
100
sub post_store
101
{
102
my $self = shift;
103
104
my @links = $self->keeper->get_links(
105
class => 'blogs::TagCloud',
106
dest_id => $self->id,
107
);
108
foreach my $link ( @links ) {
109
$link->delete();
110
}
111
if ( $self->tags ) {
112
my @tags = map { encode('utf-8', lc($_)) } split /[\t\ ]*,[\t\ ]*/, decode("utf-8", $self->tags);
113
foreach my $tag ( @tags ) {
114
my ($tid) = $self->keeper->get_documents(
115
name => $tag,
116
ilike => 1,
117
class => 'blogs::Tag',
118
ids => 1,
119
limit => 1,
120
);
121
unless ( $tid ) {
122
my $tobj = blogs::Tag->new( $self->keeper );
123
$tobj->name( $tag );
124
$tobj->status( 1 );
125
$tobj->store;
126
$tid = $tobj->id;
127
}
128
my $link = blogs::TagCloud->new( $keeper );
129
$link->status( 1 );
130
$link->source_id( $tid );
131
$link->source_class( 'blogs::Tag' );
132
$link->dest_id( $self->id );
133
$link->dest_class( $self->class );
134
$link->store;
135
}
136
}
137
138
1;
139
}
140
141
142
sub pre_delete
143
{
144
my $self = shift;
145
146
my $comments_db_table = blogs::SQL::CommentsTable->db_table;
147
my $sthcom = $self->keeper->SQL->prepare( "delete from $comments_db_table where record_id = ?" );
148
$sthcom->execute( $self->id );
149
$sthcom->finish;
150
151
my $tags_db_table = blogs::SQL::TagsTable->db_table;
152
my $sthtag = $self->keeper->SQL->prepare( "delete from $comments_db_table where dest_id = ? and dest_class = ?" );
153
$sthtag->execute( $self->id, $self->class );
154
$sthtag->finish;
155
156
1;
157
}
158
159
1;
utf8/plugins/blogs/lib/blogs/RecordSection.pm
1
package blogs::RecordSection;
2
3
use base 'Contenido::Section';
4
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'filters', 'hidden' => 1 },
9
{ 'attr' => 'default_document_class', 'default' => 'blogs::Record' },
10
# { 'attr' => 'brief', 'type' => 'text', 'rusname' => 'Описание секции' },
11
)
12
}
13
14
sub class_name
15
{
16
return 'Секция записей в блогах';
17
}
18
19
sub class_description
20
{
21
return 'Секция записей в блогах';
22
}
23
24
1;
utf8/plugins/blogs/lib/blogs/SQL/BlogsTable.pm
1
package blogs::SQL::BlogsTable;
2
3
use strict;
4
use base 'SQL::DocumentTable';
5
6
sub db_table
7
{
8
return 'blogs';
9
}
10
11
sub available_filters {
12
my @available_filters = qw(
13
_class_filter
14
_status_filter
15
_in_id_filter
16
_id_filter
17
_name_filter
18
_class_excludes_filter
19
_sfilter_filter
20
_datetime_filter
21
_date_equal_filter
22
_date_filter
23
_previous_days_filter
24
_s_filter
25
26
_excludes_filter
27
_link_filter
28
_uid_filter
29
_type_filter
30
);
31
return \@available_filters;
32
}
33
34
# ----------------------------------------------------------------------------
35
# Свойства храним в массивах, потому что порядок важен!
36
# Это общие свойства - одинаковые для всех документов.
37
#
38
# attr - обязательный параметр, название атрибута;
39
# type - тип аттрибута, требуется для отображдения;
40
# rusname - русское название, опять же требуется для отображения;
41
# hidden - равен 1, когда
42
# readonly - инициализации при записи только без изменения в дальнейшем
43
# db_field - поле в таблице
44
# default - значение по умолчанию (поле всегда имеет это значение)
45
# ----------------------------------------------------------------------------
46
sub required_properties
47
{
48
my $self = shift;
49
50
my @parent_properties = $self->SUPER::required_properties;
51
return (
52
@parent_properties,
53
{ # Тип блога...
54
'attr' => 'type',
55
'type' => 'status',
56
'cases' => [
57
[0, 'Личный блог'],
58
[1, 'Групповой блог'],
59
],
60
'rusname' => 'Пользователь',
61
'db_field' => 'type',
62
'db_type' => 'integer',
63
'db_opts' => 'default 0',
64
},
65
{ # Пользователь...
66
'attr' => 'uid',
67
'type' => 'integer',
68
'rusname' => 'Пользователь',
69
'db_field' => 'uid',
70
'db_type' => 'integer',
71
'db_opts' => 'not null',
72
},
73
{ # Количество записей...
74
'attr' => 'records',
75
'type' => 'checkbox',
76
'rusname' => 'Количество записей в блоге',
77
'hidden' => 1,
78
'db_field' => 'records',
79
'db_type' => 'integer',
80
'db_opts' => 'not null',
81
},
82
{ # Участники...
83
'attr' => 'members',
84
'type' => 'text',
85
'rusname' => 'Участники',
86
'hidden' => 1,
87
'db_field' => 'members',
88
'db_type' => 'integer[]',
89
},
90
{ # Читатели...
91
'attr' => 'readers',
92
'type' => 'text',
93
'rusname' => 'Читатели',
94
'hidden' => 1,
95
'db_field' => 'readers',
96
'db_type' => 'integer[]',
97
},
98
{ # Модераторы...
99
'attr' => 'moderators',
100
'type' => 'checkbox',
101
'rusname' => 'Модераторы',
102
'hidden' => 1,
103
'db_field' => 'moderators',
104
'db_type' => 'integer[]',
105
},
106
);
107
}
108
109
sub _uid_filter {
110
my ($self,%opts)=@_;
111
return undef unless ( exists $opts{uid} && $opts{uid} );
112
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
113
}
114
115
sub _type_filter {
116
my ($self,%opts)=@_;
117
return undef unless ( exists $opts{type} );
118
return &SQL::Common::_generic_int_filter('d.type', $opts{type});
119
}
120
121
122
sub _link_filter {
123
my ($self,%opts)=@_;
124
125
my @wheres=();
126
my @binds=();
127
128
# Связь определенного класса
129
if (exists($opts{lclass})) {
130
my ($where, $values) = SQL::Common::_generic_text_filter('l.class', $opts{lclass});
131
push (@wheres, $where);
132
push @binds, (ref $values ? @$values : $values) if defined $values;
133
}
134
135
my $lclass = $opts{lclass} || 'Contenido::Link';
136
my $link_table = $lclass->_get_table->db_table();
137
138
# Ограничение по статусу связи
139
if ( exists $opts{lstatus} ) {
140
my ($where, $values) = SQL::Common::_generic_int_filter('l.status', $opts{lstatus});
141
push (@wheres, $where);
142
push (@binds, ref($values) ? @$values:$values) if (defined $values);
143
}
144
145
# Связь с определенным документ(ом/тами) по цели линка
146
if ( exists $opts{ldest} ) {
147
my ($where, $values) = SQL::Common::_generic_int_filter('l.dest_id', $opts{ldest});
148
push (@wheres, $where);
149
push (@binds, ref($values) ? @$values:$values) if (defined $values);
150
if ( $self->_single_class || $opts{lclass} eq 'blogs::MemberLink' ) {
151
return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id");
152
} else {
153
return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id and l.source_class=d.class");
154
}
155
}
156
157
# Связь с определенным документ(ом/тами) по источнику линка
158
if ( exists $opts{lsource} ) {
159
my ($where, $values) = SQL::Common::_generic_int_filter('l.source_id', $opts{lsource});
160
push (@wheres, $where);
161
push (@binds, ref($values) ? @$values:$values) if (defined $values);
162
if ( $self->_single_class || $opts{lclass} eq 'blogs::MemberLink' ) {
163
return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id");
164
} else {
165
return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id and l.dest_class=d.class");
166
}
167
}
168
169
return (undef);
170
}
171
172
173
1;
174
utf8/plugins/blogs/lib/blogs/SQL/MembersTable.pm
1
package blogs::SQL::MembersTable;
2
3
use strict;
4
use Contenido::Globals;
5
use base 'SQL::LinkTable';
6
7
sub db_table
8
{
9
return 'blog_members';
10
}
11
12
sub available_filters {
13
my @available_filters = qw(
14
_class_filter
15
_status_filter
16
_in_id_filter
17
_id_filter
18
_name_filter
19
_class_excludes_filter
20
_excludes_filter
21
_datetime_filter
22
_date_equal_filter
23
_date_filter
24
25
_dest_id_filter
26
_dest_class_filter
27
_source_id_filter
28
_source_class_filter
29
);
30
return \@available_filters;
31
}
32
33
# ----------------------------------------------------------------------------
34
# Свойства храним в массивах, потому что порядок важен!
35
# Это общие свойства - одинаковые для всех документов.
36
#
37
# attr - обязательный параметр, название атрибута;
38
# type - тип аттрибута, требуется для отображдения;
39
# rusname - русское название, опять же требуется для отображения;
40
# hidden - равен 1, когда
41
# readonly - инициализации при записи только без изменения в дальнейшем
42
# db_field - поле в таблице
43
# default - значение по умолчанию (поле всегда имеет это значение)
44
# ----------------------------------------------------------------------------
45
sub required_properties
46
{
47
my $self = shift;
48
49
my @parent_properties = grep { $_->{attr} ne 'source_class' } $self->SUPER::required_properties;
50
return (
51
@parent_properties,
52
{ # Is Moder
53
'attr' => 'moderator',
54
'type' => 'integer',
55
'rusname' => 'Является модератором',
56
'db_field' => 'moderator',
57
'db_type' => 'smallint',
58
'default' => 0,
59
},
60
);
61
}
62
63
########### FILTERS DESCRIPTION ####################################################################################
64
65
66
1;
67
utf8/plugins/blogs/lib/blogs/SQL/RecordsTable.pm
1
package blogs::SQL::RecordsTable;
2
3
use strict;
4
use base 'SQL::DocumentTable';
5
6
sub db_table
7
{
8
return 'blog_records';
9
}
10
11
sub available_filters {
12
my @available_filters = qw(
13
_class_filter
14
_status_filter
15
_in_id_filter
16
_id_filter
17
_name_filter
18
_class_excludes_filter
19
_sfilter_filter
20
_datetime_filter
21
_date_equal_filter
22
_date_filter
23
_previous_days_filter
24
_s_filter
25
26
_excludes_filter
27
_link_filter
28
_uid_filter
29
_blog_id_filter
30
_tags_filter
31
_public_records_filter
32
);
33
return \@available_filters;
34
}
35
36
# ----------------------------------------------------------------------------
37
# Свойства храним в массивах, потому что порядок важен!
38
# Это общие свойства - одинаковые для всех документов.
39
#
40
# attr - обязательный параметр, название атрибута;
41
# type - тип аттрибута, требуется для отображдения;
42
# rusname - русское название, опять же требуется для отображения;
43
# hidden - равен 1, когда
44
# readonly - инициализации при записи только без изменения в дальнейшем
45
# db_field - поле в таблице
46
# default - значение по умолчанию (поле всегда имеет это значение)
47
# ----------------------------------------------------------------------------
48
sub required_properties
49
{
50
my $self = shift;
51
52
my @parent_properties = $self->SUPER::required_properties;
53
return (
54
@parent_properties,
55
{ # Пользователь...
56
'attr' => 'uid',
57
'type' => 'integer',
58
'rusname' => 'Пользователь',
59
'db_field' => 'uid',
60
'db_type' => 'integer',
61
'db_opts' => 'not null',
62
},
63
{ # Блог...
64
'attr' => 'blog_id',
65
'type' => 'integer',
66
'rusname' => 'Блог',
67
'db_field' => 'blog_id',
68
'db_type' => 'integer',
69
'db_opts' => 'not null',
70
},
71
{ # Количество записей...
72
'attr' => 'comments',
73
'type' => 'integer',
74
'rusname' => 'Количество комментариев',
75
'db_field' => 'comments',
76
'db_type' => 'integer',
77
'db_opts' => 'default 0',
78
},
79
{
80
'attr' => 'tags',
81
'type' => 'string',
82
'rusname' => 'Теги (через запятую)',
83
'shortname' => 'Теги',
84
'db_field' => 'tags',
85
'db_type' => 'text',
86
},
87
);
88
}
89
90
sub _uid_filter {
91
my ($self,%opts)=@_;
92
return undef unless ( exists $opts{uid} && $opts{uid} );
93
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
94
}
95
96
sub _blog_id_filter {
97
my ($self,%opts)=@_;
98
return undef unless ( exists $opts{blog_id} );
99
return &SQL::Common::_generic_int_filter('d.blog_id', $opts{blog_id});
100
}
101
102
sub _tags_filter {
103
my ($self,%opts)=@_;
104
return undef unless ( exists $opts{tags} );
105
my (@wheres, @binds, $join);
106
107
my $link_table = blogs::SQL::TagCloudTable->db_table;
108
my $tag_table = blogs::SQL::TagsTable->db_table;
109
$join = " join $link_table as l on l.dest_id=d.id and l.dest_class=d.class";
110
push @wheres, "l.source_id in (select id from $tag_table where name ilike ?)";
111
push @binds, $opts{tags};
112
return (\@wheres, \@binds, $join);
113
}
114
115
sub _public_records_filter {
116
my ($self,%opts)=@_;
117
return undef unless ( exists $opts{public} );
118
119
my ($where, $values, $joins) = ([], [], []);
120
push @$joins, ' INNER JOIN blogs AS b ON d.blog_id=b.id ';
121
push @$where, ' b.type = ? ';
122
push @$values, $opts{public};
123
124
return ($where, $values, $joins);
125
}
126
127
sub _get_orders {
128
my ($self, %opts) = @_;
129
130
if ($opts{order_by}) {
131
return ' order by '.$opts{order_by};
132
} else {
133
return ' order by dtime desc';
134
}
135
return undef;
136
}
137
138
1;
139
utf8/plugins/blogs/lib/blogs/SQL/TagCloudTable.pm
1
package blogs::SQL::TagCloudTable;
2
3
use strict;
4
use Contenido::Globals;
5
use base 'SQL::LinkTable';
6
7
sub db_table
8
{
9
return 'blog_tag_cloud';
10
}
11
12
sub available_filters {
13
my @available_filters = qw(
14
_class_filter
15
_status_filter
16
_in_id_filter
17
_id_filter
18
_class_excludes_filter
19
_excludes_filter
20
_datetime_filter
21
_date_equal_filter
22
_date_filter
23
24
_dest_id_filter
25
_dest_class_filter
26
_source_id_filter
27
_source_class_filter
28
);
29
return \@available_filters;
30
}
31
32
# ----------------------------------------------------------------------------
33
# Свойства храним в массивах, потому что порядок важен!
34
# Это общие свойства - одинаковые для всех документов.
35
#
36
# attr - обязательный параметр, название атрибута;
37
# type - тип аттрибута, требуется для отображдения;
38
# rusname - русское название, опять же требуется для отображения;
39
# hidden - равен 1, когда
40
# readonly - инициализации при записи только без изменения в дальнейшем
41
# db_field - поле в таблице
42
# default - значение по умолчанию (поле всегда имеет это значение)
43
# ----------------------------------------------------------------------------
44
sub required_properties
45
{
46
my $self = shift;
47
48
my @parent_properties = $self->SUPER::required_properties;
49
return (
50
@parent_properties,
51
);
52
}
53
54
########### FILTERS DESCRIPTION ####################################################################################
55
56
57
1;
utf8/plugins/blogs/lib/blogs/SQL/TagsTable.pm
1
package blogs::SQL::TagsTable;
2
3
use strict;
4
use base 'SQL::DocumentTable';
5
6
sub db_table
7
{
8
return 'blog_tags';
9
}
10
11
sub available_filters {
12
my @available_filters = qw(
13
_class_filter
14
_status_filter
15
_in_id_filter
16
_id_filter
17
_name_filter
18
_class_excludes_filter
19
_sfilter_filter
20
_s_filter
21
22
_excludes_filter
23
_link_filter
24
_uid_filter
25
);
26
return \@available_filters;
27
}
28
29
# ----------------------------------------------------------------------------
30
# Свойства храним в массивах, потому что порядок важен!
31
# Это общие свойства - одинаковые для всех документов.
32
#
33
# attr - обязательный параметр, название атрибута;
34
# type - тип аттрибута, требуется для отображдения;
35
# rusname - русское название, опять же требуется для отображения;
36
# hidden - равен 1, когда
37
# readonly - инициализации при записи только без изменения в дальнейшем
38
# db_field - поле в таблице
39
# default - значение по умолчанию (поле всегда имеет это значение)
40
# ----------------------------------------------------------------------------
41
sub required_properties
42
{
43
my $self = shift;
44
45
my @parent_properties = grep { $_->{attr} ne 'dtime' && $_->{attr} ne 'sections' } $self->SUPER::required_properties;
46
return (
47
@parent_properties,
48
{ # Пользователь...
49
'attr' => 'uid',
50
'type' => 'integer',
51
'rusname' => 'Пользователь',
52
'hidden' => 1,
53
'db_field' => 'uid',
54
'db_type' => 'integer',
55
'db_opts' => 'not null',
56
},
57
{ # Количество записей...
58
'attr' => 'rate',
59
'type' => 'checkbox',
60
'rusname' => 'Количество упоминаний в блогах',
61
'hidden' => 1,
62
'default' => 0,
63
'db_field' => 'rate',
64
'db_type' => 'integer',
65
'db_opts' => 'default 0',
66
},
67
{ # Массив секций, обрабатывается специальным образом...
68
'attr' => 'sections',
69
'type' => 'sections_list',
70
'rusname' => 'Секции',
71
'hidden' => 1,
72
'db_field' => 'sections',
73
'db_type' => 'integer',
74
},
75
);
76
}
77
78
79
sub _uid_filter {
80
my ($self,%opts)=@_;
81
return undef unless ( exists $opts{uid} && $opts{uid} );
82
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
83
}
84
85
86
sub _s_filter {
87
my ($self,%opts)=@_;
88
return undef unless ( exists $opts{s} );
89
return &SQL::Common::_generic_int_filter('d.sections', $opts{s});
90
}
91
92
93
sub _link_filter {
94
my ($self,%opts)=@_;
95
96
my @wheres=();
97
my @binds=();
98
99
# Связь определенного класса
100
if (exists($opts{lclass})) {
101
my ($where, $values) = SQL::Common::_generic_text_filter('l.class', $opts{lclass});
102
push (@wheres, $where);
103
push @binds, (ref $values ? @$values : $values) if defined $values;
104
}
105
106
my $lclass = $opts{lclass} || 'Contenido::Link';
107
my $link_table = $lclass->_get_table->db_table();
108
109
# Ограничение по статусу связи
110
if ( exists $opts{lstatus} ) {
111
my ($where, $values) = SQL::Common::_generic_int_filter('l.status', $opts{lstatus});
112
push (@wheres, $where);
113
push (@binds, ref($values) ? @$values:$values) if (defined $values);
114
}
115
116
# Связь с определенным документ(ом/тами) по цели линка
117
if ( exists $opts{ldest} ) {
118
my ($where, $values) = SQL::Common::_generic_int_filter('l.dest_id', $opts{ldest});
119
push (@wheres, $where);
120
push (@binds, ref($values) ? @$values:$values) if (defined $values);
121
if ( $self->_single_class || $opts{lclass} eq 'blogs::TagCloud' ) {
122
return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id");
123
} else {
124
return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id and l.source_class=d.class");
125
}
126
}
127
128
# Связь с определенным документ(ом/тами) по источнику линка
129
if ( exists $opts{lsource} ) {
130
my ($where, $values) = SQL::Common::_generic_int_filter('l.source_id', $opts{lsource});
131
push (@wheres, $where);
132
push (@binds, ref($values) ? @$values:$values) if (defined $values);
133
if ( $self->_single_class || $opts{lclass} eq 'blogs::TagCloud' ) {
134
return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id");
135
} else {
136
return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id and l.dest_class=d.class");
137
}
138
}
139
140
return (undef);
141
}
142
143
144
1;
145
utf8/plugins/blogs/lib/blogs/State.pm.proto
1
package blogs::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
# зашитая конфигурация плагина
15
$self->{db_type} = 'none';
16
$self->{db_keepalive} = 0;
17
$self->{db_host} = '';
18
$self->{db_name} = '';
19
$self->{db_user} = '';
20
$self->{db_password} = '';
21
$self->{db_port} = '';
22
23
$self->{data_directory} = '';
24
$self->{images_directory} = '';
25
$self->{binary_directory} = '';
26
$self->{preview} = '';
27
$self->{debug} = '';
28
$self->{store_method} = '';
29
$self->{cascade} = '';
30
$self->{memcached_enable} = '';
31
32
$self->_init_();
33
$self;
34
}
35
36
sub info {
37
my $self = shift;
38
return unless ref $self;
39
40
for (sort keys %{$self->{attributes}}) {
41
my $la = length $_;
42
warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n";
43
}
44
}
45
46
sub _init_ {
47
my $self = shift;
48
49
# зашитая конфигурация плагина
50
$self->{attributes}->{$_} = 'SCALAR' for qw(
51
db_type
52
db_keepalive
53
db_host
54
db_port
55
db_name
56
db_user
57
db_password
58
data_directory images_directory binary_directory preview debug store_method cascade memcached_enable
59
);
60
}
61
62
sub AUTOLOAD {
63
my $self = shift;
64
my $attribute = $AUTOLOAD;
65
66
$attribute =~ s/.*:://;
67
return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY
68
69
if (!exists $self->{attributes}->{$attribute}) {
70
warn "Contenido Error (blogs::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n";
71
return;
72
}
73
74
$self->{$attribute} = shift @_ if $#_>=0;
75
$self->{$attribute};
76
}
77
78
1;
utf8/plugins/blogs/lib/blogs/SubscriberLink.pm
1
package blogs::SubscriberLink;
2
3
use strict;
4
use vars qw(@ISA);
5
6
use Contenido::Link;
7
@ISA = ('Contenido::Link');
8
9
sub extra_properties
10
{
11
return (
12
{ 'attr' => 'moderator', 'hidden' => 1, },
13
);
14
}
15
16
sub class_name
17
{
18
return 'Подписчик блога';
19
}
20
21
sub class_description
22
{
23
return 'Связь между пользователями и блогами';
24
}
25
26
#sub available_sources
27
#{
28
# return [ qw(formula1::Championship) ];
29
#}
30
31
sub available_destinations
32
{
33
return [ qw(blogs::Blog) ];
34
}
35
36
sub class_table
37
{
38
return 'blogs::SQL::MembersTable';
39
}
40
41
1;
utf8/plugins/blogs/lib/blogs/Tag.pm
1
package blogs::Tag;
2
3
use base "Contenido::Document";
4
use Contenido::Globals;
5
6
sub extra_properties
7
{
8
return (
9
{ 'attr' => 'name', 'type' => 'string', 'rusname' => 'Тег' },
10
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус записи',
11
'cases' => [
12
[0, 'Тег не активен'],
13
[1, 'Тег активен'],
14
],
15
},
16
)
17
}
18
19
sub class_name
20
{
21
return 'Тег для блогов';
22
}
23
24
sub class_description
25
{
26
return 'Тег для блогов';
27
}
28
29
30
sub class_table
31
{
32
return 'blogs::SQL::TagsTable';
33
}
34
35
#sub search_fields
36
#{
37
# return ('name');
38
#}
39
40
sub pre_store
41
{
42
my $self = shift;
43
44
my $default_section = $project->s_alias->{blog_cloud} if ref $project->s_alias eq 'HASH' && exists $project->s_alias->{blog_cloud};
45
$self->sections( $default_section ) unless $self->sections;
46
47
unless ( $self->id ) {
48
if ( $user && ref $user ) {
49
$self->uid( $user->id );
50
} elsif ( $session && ref $session eq 'HASH' && exists $session->{id} ) {
51
$self->uid( $session->{id} );
52
}
53
}
54
55
return 1;
56
}
57
58
sub pre_delete
59
{
60
my $self = shift;
61
62
my $comments_db_table = blogs::SQL::TagCloudTable->db_table;
63
my $sthcom = $self->keeper->SQL->prepare( "delete from $comments_db_table where source_id = ?" );
64
$sthcom->execute( $self->id );
65
$sthcom->finish;
66
67
1;
68
}
69
70
1;
utf8/plugins/blogs/lib/blogs/TagCloud.pm
1
package blogs::TagCloud;
2
3
use strict;
4
use vars qw(@ISA);
5
6
use base 'Contenido::Link';
7
8
sub extra_properties
9
{
10
return (
11
);
12
}
13
14
sub class_name
15
{
16
return 'Элемент облака тегов';
17
}
18
19
sub class_description
20
{
21
return 'Связь между тегом и записью в блоге';
22
}
23
24
sub available_sources
25
{
26
return [ qw(blogs::Tag) ];
27
}
28
29
sub available_destinations
30
{
31
return [ qw(blogs::Record) ];
32
}
33
34
sub class_table
35
{
36
return 'blogs::SQL::TagCloudTable';
37
}
38
39
1;
utf8/plugins/blogs/lib/blogs/TagSection.pm
1
package blogs::TagSection;
2
3
use base 'Contenido::Section';
4
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'filters', 'hidden' => 1 },
9
{ 'attr' => 'default_document_class', 'default' => 'blogs::Tag' },
10
# { 'attr' => 'brief', 'type' => 'text', 'rusname' => 'Описание секции' },
11
)
12
}
13
14
sub class_name
15
{
16
return 'Секция тегов в блогах';
17
}
18
19
sub class_description
20
{
21
return 'Секция тегов в блогах';
22
}
23
24
1;
utf8/plugins/blogs/sql/TOAST/blog_members.sql
1
create table blog_members
2
(
3
id integer not null primary key default nextval('public.documents_id_seq'::text),
4
class text not null,
5
ctime timestamp not null default now(),
6
mtime timestamp not null default now(),
7
btime timestamp not null default now(),
8
etime timestamp not null default now(),
9
status smallint not null default 1,
10
source_id integer not null,
11
moderator smallint default 0,
12
temporary smallint default 0,
13
dest_id integer not null,
14
dest_class text not null default 'blogs::Blog',
15
data text
16
);
17
create index blog_members_source on blog_members (source_id);
18
create index blog_members_dest on blog_members (dest_id);
utf8/plugins/blogs/sql/TOAST/blog_tag_cloud.sql
1
create table blog_tag_cloud
2
(
3
id integer not null primary key default nextval('public.documents_id_seq'::text),
4
class text not null,
5
ctime timestamp not null default now(),
6
mtime timestamp not null default now(),
7
status smallint not null default 1,
8
source_id integer not null,
9
source_class text default 'blogs::Tag',
10
dest_id integer not null,
11
dest_class text not null default 'blogs::Record',
12
data text
13
);
14
create index blog_tag_cloud_source on blog_tag_cloud (source_id);
15
create index blog_tag_cloud_dest on blog_tag_cloud (dest_id);
utf8/plugins/blogs/sql/TOAST/blog_tags.sql
1
create table blog_tags
2
(
3
id integer not null primary key default nextval('public.documents_id_seq'::text),
4
class text not null,
5
ctime timestamp not null default now(),
6
mtime timestamp not null default now(),
7
status smallint not null default 0,
8
uid integer not null,
9
sections integer,
10
name text,
11
rate integer default 0,
12
data text
13
);
14
create index blog_tags_sections on blog_tags (sections);
15
create index blog_tags_uid on blog_tags (uid);
utf8/plugins/blogs/sql/TOAST/blogs.sql
1
create table blogs
2
(
3
id integer not null primary key default nextval('public.documents_id_seq'::text),
4
class text not null,
5
ctime timestamp not null default now(),
6
mtime timestamp not null default now(),
7
dtime timestamp not null default now(),
8
status smallint not null default 0,
9
type smallint default 0,
10
uid integer not null,
11
sections integer[],
12
name text,
13
records integer default 0,
14
readers integer[],
15
members integer[],
16
moderators integer[],
17
data text
18
);
19
create index blogs_sections on blogs using gist ( "sections" "gist__int_ops" );
20
create index blogs_members on blogs using gist ( "members" "gist__int_ops" );
21
create index blogs_moderators on blogs using gist ( "moderators" "gist__int_ops" );
22
create index blogs_dtime on blogs (dtime);
23
create index blogs_uid on blogs (uid);
utf8/plugins/blogs/sql/TOAST/records.sql
1
create table blog_records
2
(
3
id integer not null primary key default nextval('public.documents_id_seq'::text),
4
class text not null,
5
ctime timestamp not null default now(),
6
mtime timestamp not null default now(),
7
dtime timestamp not null default now(),
8
status smallint not null default 0,
9
uid integer not null,
10
blog_id integer not null,
11
sections integer[],
12
name text,
13
comments integer default 0,
14
tags text,
15
data text
16
);
17
create index blog_records_sections on blog_records using gist ( "sections" "gist__int_ops" );
18
create index blog_records_dtime on blog_records (dtime);
19
create index blog_records_uid on blog_records (uid);
20
create index blog_records_blog_id on blog_records (blog_id);
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.