Revision 630
Date:
2016/12/09 15:54:41
Author:
ahitrov
Revision Log:
Tag editing form
Files:
Legend:
Added
Removed
Modified
utf8/plugins/tag/comps/contenido/tag/ajax/tag_edit_form.html
1
1
% if ( ref $tag ) {
2
% if ( $tag->id ) {
2
3
<fieldset>
3
4
<legend><% $tag->id ? 'Редактировать' : 'Создать' %> тег</legend>
4
5
<& /contenido/tag/components/form_tag_edit.msn, object => $tag &>
5
6
</fieldset>
7
% } else {
8
<fieldset>
9
<legend><% $tag->id ? 'Редактировать' : 'Создать' %> тег</legend>
10
<& /contenido/tag/components/form_tag_edit.msn, object => $tag &>
11
</fieldset>
12
% }
6
13
% } else {
7
14
<div class="error-prompt">Не найден тег с ID=<% $id |h %></div>
8
15
% }
utf8/plugins/tag/comps/contenido/tag/ajax/tag_store.html
1
<% $json %>
2
<%once>
3
4
use JSON::XS;
5
6
</%once>
7
<%args>
8
9
$id => undef
10
11
</%args>
12
<%init>
13
14
my %result;
15
16
warn Dumper \%ARGS;
17
if ( !$id || $id && $id =~ /^\d+$/ && $id > 0 ) {
18
my $tag;
19
if ( $id ) {
20
$tag = $keeper->get_document_by_id( $id, class => 'tag::Tag' );
21
} else {
22
$tag = tag::Tag->new( $keeper );
23
}
24
if ( ref $tag ) {
25
my $dup = $keeper->get_documents(
26
$tag->id ? ( excludes => $tag->id ) : (),
27
name => $ARGS{name},
28
ilike => 1,
29
class => 'tag::Tag',
30
count => 1,
31
);
32
if ( $dup ) {
33
$result{error} = Encode::decode('utf-8', 'Найден дубликат тега с таким названием');
34
} else {
35
$tag->name( $ARGS{name} );
36
$tag->alias( $ARGS{alias} );
37
$tag->status( $ARGS{status} );
38
$m->comp('/contenido/tag/components/outputs/tag_tree.msn', SETS => \%ARGS, object => $tag);
39
if ( $tag->store ) {
40
$result{success} = 1;
41
$result{id} = $tag->id;
42
} else {
43
$result{error} = Encode::decode('utf-8', 'Системная ошибка сохранения. Смотри логи');
44
}
45
}
46
} else {
47
$result{error} = Encode::decode('utf-8', 'Тег с данным идентификатором не найден');
48
}
49
} else {
50
$result{error} = Encode::decode('utf-8', 'Неверно указан идентификатор');
51
}
52
53
my $json = encode_json \%result;
54
# $r->content_type('application/json');
55
56
</%init>
utf8/plugins/tag/comps/contenido/tag/components/block_tag_list.msn
1
1
% if ( @$tags ) {
2
<script type="text/javascript">
3
<!--
4
$(document).ready(function(){
5
$('.js-edit-tag').on('click', function( ev ){
6
ev.preventDefault();
7
OpenContentForm( $(this).data('id') );
8
});
9
10
});
11
//-->
12
</script>
2
13
<table width="100%" border="0" cellpadding="4" cellspacing="0" class="tlistdocs">
3
14
<tr bgcolor="#efefef">
4
15
<th>id</th>
…
…
9
20
</tr>
10
21
% foreach my $tag ( @$tags ) {
11
22
<tr valign="top">
12
<td><% $tag->id %></td>
13
<td><% $tag->name %></td>
23
<td><a href="?id=<% $tag->id %>" class="status-<% $tag->status %> js-edit-tag" data-id="<% $tag->id %>"><% $tag->id %></a></td>
24
<td><a href="?id=<% $tag->id %>" class="status-<% $tag->status %> js-edit-tag" data-id="<% $tag->id %>"><% $tag->name %></a></td>
14
25
<td><% $tag->alias %></td>
15
26
<td></td>
16
27
<td></td>
utf8/plugins/tag/comps/contenido/tag/components/form_tag_edit.msn
1
<script type="text/javascript">
2
<!--
3
$(document).ready(function(){
4
$('#tag-edit-submit').on('click', function(ev){
5
ev.preventDefault();
6
var oForm = document.forms['tag-edit-form'];
7
if ( oForm.elements['name'].value == '' ) {
8
$('#tag-edit-error-prompt').text('Не указано название тега').show();
9
oForm.elements['name'].focus();
10
return false;
11
}
12
var nId = oForm.elements['id'].value;
13
var sName = oForm.elements['name'].value;
14
var sAlias = oForm.elements['alias'].value;
15
var nStatus = oForm.elements['status'].value;
16
var nPid = oForm.elements['pid'].value;
17
$('#tag-edit-error-prompt').hide();
18
$.ajax({
19
'url' : '/contenido/tag/ajax/tag_store.html',
20
'data' : { 'id' : nId, 'name' : sName, 'alias' : sAlias, 'status' : nStatus, 'pid' : nPid },
21
'type' : 'POST',
22
'dataType' : "json",
23
'success' : function( data ) {
24
if ( data.error ) {
25
$('#tag-edit-error-prompt').text(data.error).show();
26
}
27
if ( data.success ) {
28
nTagsFormEditing = 0;
29
OpenContentForm( data.id );
30
}
31
},
32
'error' : function(XMLHttpRequest, textStatus) {
33
alert(textStatus);
34
}
35
});
36
});
37
});
38
//-->
39
</script>
1
40
<form enctype="multipart/form-data" method="POST" name="tag-edit-form">
2
41
<input type="hidden" name="id" value="<% $object->id %>">
42
% if ( $object->id ) {
43
<div class="form-field">
44
<div class="prompt">ID: <span class="value"><% $object->id %></span></div>
45
</div>
46
% }
47
% foreach my $name ( qw( name alias status ) ) {
48
% my $prop = $props{$name};
49
% my $type = $prop->{type};
50
% if ( $m->comp_exists( "/contenido/components/inputs/$type.msn" ) ) {
51
<div class="form-field">
52
<div class="prompt"><% $prop->{rusname} %><span class="desc">| Attr=<% $prop->{attr} %>; Type=<% $prop->{type} %></span></div>
53
<& "/contenido/components/inputs/$type.msn", object => $object, prop => $prop, name => $name, check => $object->$name &>
54
</div>
55
% }
56
% }
57
% if ( $state->{tag}->tag_structure eq 'tree' ) {
58
% my $name = 'pid';
59
% my $prop = $props{$name};
60
<div class="form-field">
61
<div class="prompt"><% $prop->{rusname} %><span class="desc">| Attr=<% $prop->{attr} %></span></div>
62
<& /contenido/tag/components/inputs/tag_tree.msn, object => $object, name => 'pid', prop => $prop &>
63
% }
64
</div>
65
<div id="tag-edit-error-prompt" class="error-prompt" style="display:none;"></div>
66
<div class="form-submit">
67
<input id="tag-edit-submit" type="button" class="input_btn" value="Сохранить">
68
</div>
3
69
</form>
4
70
<%args>
5
71
…
…
10
76
11
77
return unless ref $object;
12
78
my @props = $object->structure;
79
my %props = map { $_->{attr} => $_ } @props;
13
80
14
81
</%init>
utf8/plugins/tag/comps/contenido/tag/components/inputs/tag_tree.msn
1
<select name="<% $name %>" style="width:37%" autocomplete="off">
2
<option value="-1" style="color:red">Тег вне иерархии</option>
3
<option value="0" style="color:blue"<% $check == 0 && $object->level > 0 ? ' selected' : '' %>>Верхний уровень</option>
4
% if ( exists $tree->{root} && scalar @{$tree->{root}} ) {
5
% foreach my $tag ( @{$tree->{root}} ) {
6
% my $selected = $object->pid && $tag->id == $object->pid ? ' selected' : '';
7
<option value="<% $tag->id %>" style="padding-left:20px;<% $selected ? 'font-weight:bold;' : '' %>"<% $selected %>><% $tag->name.($tag->alias ? ' ('.$tag->alias.')' : '') %></option>
8
% }
9
% }
10
</select>
11
<%args>
12
13
$object => undef
14
$name => 'pid'
15
$prop => undef
16
17
</%args>
18
<%init>
19
20
return unless ref $object;
21
($prop) = grep { $_->{attr} eq $name } $object->structure unless ref $prop;
22
my $tree = $keeper->{tag}->get_tree( level => 3 );
23
my $check = $object->$name;
24
25
</%init>
utf8/plugins/tag/comps/contenido/tag/components/outputs/tag_tree.msn
1
<%args>
2
3
$SETS => {}
4
$object => undef
5
6
</%args>
7
<%init>
8
9
return unless ref $object;
10
return unless exists $SETS->{pid} && defined $SETS->{pid};
11
12
my $pid = $SETS->{pid};
13
if ( $pid == 0 ) {
14
$object->pid( 0 );
15
$object->level( 1 );
16
} elsif ( $pid < 0 ) {
17
$object->pid( 0 );
18
$object->level( 0 );
19
} elsif ( $pid > 0 ) {
20
my $tag = $keeper->get_document_by_id( $pid, class => 'tag::Tag' );
21
if ( ref $tag ) {
22
$object->pid( $pid );
23
$object->level( $tag->level + 1 );
24
} else {
25
$object->pid( 0 );
26
$object->level( 0 );
27
}
28
}
29
30
</%init>
utf8/plugins/tag/comps/contenido/tag/i/css/styles.css
2
2
.tag-plugin .block-add-link a { text-decoration:none; }
3
3
.tag-plugin .block-add-link a:hover { text-decoration:underline; }
4
4
5
.tag-plugin a.status-0 { color:gray; }
6
.tag-plugin a.status-1 { color:#0000ee; }
7
.tag-plugin a.status-3 { color:red; }
8
.tag-plugin .error-prompt { border:1px solid red; padding:10px; color:red; font-size:90%; }
5
9
6
.tag-plugin .error-prompt { border:1px solid red; padding:10px; color:red; }
utf8/plugins/tag/lib/tag/Cloud.pm
72
72
}
73
73
}
74
74
} else {
75
warn "Tag Cloud update error: cloud_element_id=".$self->id.", no source or destination available\n";
75
warn "Tag Cloud update error: cloud_element_id=".$self->id.", no source (".$self->source_class.", ".$self->source_id.") or destination (".$self->dest_class.", ".$self->dest_id.") available\n";
76
76
}
77
return 1;
77
78
}
78
79
79
80
sub post_delete
…
…
81
82
my $self = shift;
82
83
my $object = $keeper->get_document_by_id($self->dest_id, class => $self->dest_class ) if $self->dest_id && $self->dest_class;
83
84
my $tag = $self->keeper->get_document_by_id($self->source_id, class => $self->source_class ) if $self->source_id && $self->source_class;
84
if ( ref $object && ref $tag ) {
85
my ($prop) = grep { $_->{type} eq 'tagset' } $object->structure;
86
my $class = $object->class;
87
my $is_extra = grep { ref $_ && $_->{attr} eq $name } $class->extra_properties ? 1 : 0;
88
if ( ref $prop && !(exists $prop->{virtual} && $prop->{virtual}) ) {
89
my $name = $prop->{attr};
90
my $struct;
91
if ( ref $object->$name ) {
92
$struct = $object->$name;
93
} elsif ( $object->$name ) {
94
$struct = JSON::XS->new->utf8->decode( $object->$name );
95
}
96
if ( ref $struct eq 'ARRAY' && @$struct && (grep { $_->{id} == $tag->id } @$struct) ) {
97
@$struct = grep { $_->{id} != $tag->id } @$struct;
98
unless ( $is_extra ) {
99
$struct = Encode::encode('utf-8', JSON::XS->new->encode( $struct ));
85
if ( ref $object || ref $tag ) {
86
if ( ref $object ) {
87
my ($prop) = grep { $_->{type} eq 'tagset' } $object->structure;
88
my $class = $object->class;
89
my $is_extra = grep { ref $_ && $_->{attr} eq $name } $class->extra_properties ? 1 : 0;
90
if ( ref $prop && !(exists $prop->{virtual} && $prop->{virtual}) ) {
91
my $name = $prop->{attr};
92
my $struct;
93
if ( ref $object->$name ) {
94
$struct = $object->$name;
95
} elsif ( $object->$name ) {
96
$struct = JSON::XS->new->utf8->decode( $object->$name );
100
97
}
101
$object->$name( $struct );
102
$object->store;
98
if ( ref $struct eq 'ARRAY' && @$struct && (grep { $_->{id} == $tag->id } @$struct) ) {
99
@$struct = grep { $_->{id} != $tag->id } @$struct;
100
unless ( $is_extra ) {
101
$struct = Encode::encode('utf-8', JSON::XS->new->encode( $struct ));
102
}
103
$object->$name( $struct );
104
$object->store;
105
}
103
106
}
104
107
}
105
} else {
106
warn "Tag Cloud delete error: cloud_element_id=".$self->id.", no source or destination available\n";
107
108
}
109
if ( !ref $object || !ref $tag ) {
110
my $err = '';
111
$err .= ", no tag (".$self->source_class.", ".$self->source_id.")" unless ref $tag;
112
$err .= ", no object (".$self->dest_class.", ".$self->dest_id.")" unless ref $object;
113
warn "Tag Cloud delete warning: cloud_element_id=".$self->id.$err." available\n";
114
}
115
return 1;
108
116
}
109
117
110
118
1;
utf8/plugins/tag/lib/tag/Keeper.pm
20
20
21
21
my $tree;
22
22
my $cache_key = 'plugin_tag_tree_level_'.join('_', @$level);
23
if ( $cache && $keeper->MEMD ) {
24
$tree = $keeper->MEMD->get( $key );
23
if ( $cache > 0 && $keeper->MEMD ) {
24
$tree = $keeper->MEMD->get( $cache_key );
25
25
}
26
26
unless ( defined $tree ) {
27
27
$tree = { hash => {}, root => [] };
…
…
32
32
order_by => 'level, pid, id',
33
33
return_mode => 'array_ref',
34
34
);
35
foreach my $tag ( @$tree ) {
35
foreach my $tag ( @$tags ) {
36
36
$tag->{keeper} = undef;
37
$hash->{$tag->id} = $tag;
37
$tree->{hash}{$tag->id} = $tag;
38
38
if ( $tag->pid ) {
39
39
push @{$tree->{$tag->pid}}, $tag;
40
40
if ( exists $tree->{hash}{$tag->pid} ) {
…
…
44
44
push @{$tree->{root}}, $tag;
45
45
}
46
46
if ( $cache && $keeper->MEMD ) {
47
$keeper->MEMD->set( $key, $tree, 3600 );
47
$keeper->MEMD->set( $cache_key, $tree, 3600 );
48
48
}
49
49
}
50
50
}
utf8/plugins/tag/lib/tag/Tag.pm
7
7
{
8
8
return (
9
9
{ 'attr' => 'name', 'rusname' => 'Название тега', shortname => 'Тег' },
10
{ 'attr' => 'status',
11
cases => [
12
[0, 'Скрытый'],
13
[1, 'Активный'],
14
[-1, 'Удален'],
15
16
],
17
},
10
18
{ 'attr' => 'alt_name', type => 'string', 'rusname' => 'Альтернативная форма названия', shortname => 'Словоформа' },
11
19
)
12
20
}
utf8/plugins/tag/sql/TOAST/tags-update.630.sql
1
alter table tags alter column level set default 0;
2
update tags set level = 0 where level is null;
3
4
drop index tags_pid;
5
create index tags_pid on tags (pid) WHERE level > 0;
utf8/plugins/tag/sql/TOAST/tags.sql
7
7
status smallint not null default 0,
8
8
sections integer,
9
9
pid integer default 0,
10
level integer default 1,
10
level integer default 0,
11
11
name text,
12
12
alias text,
13
13
data text
14
14
);
15
15
create index tags_name on tags (name);
16
16
create index tags_alias on tags (alias) WHERE alias IS NOT NULL AND alias != '';
17
create index tags_pid on tags (pid);
17
create index tags_pid on tags (pid) WHERE level > 0;
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.