Revision 480
- Date:
- 2015/03/03 12:14:40
- Files:
-
- /utf8/plugins/tag/comps
- /utf8/plugins/tag/comps/contenido
- /utf8/plugins/tag/comps/contenido/components
- /utf8/plugins/tag/comps/contenido/components/inputs
- /utf8/plugins/tag/comps/contenido/components/inputs/tagset.msn (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/components/outputs
- /utf8/plugins/tag/comps/contenido/components/outputs/tagset.msn (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag
- /utf8/plugins/tag/comps/contenido/tag/ajax
- /utf8/plugins/tag/comps/contenido/tag/ajax/manage.html (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/ajax/search.html (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/autohandler (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/components
- /utf8/plugins/tag/comps/contenido/tag/components/title_inc.msn (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/dhandler (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/i
- /utf8/plugins/tag/comps/contenido/tag/i/js
- /utf8/plugins/tag/comps/contenido/tag/i/js/jquery.tagsinput.css (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/i/js/jquery.tagsinput.js (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/i/js/jquery.tagsinput.min.js (Diff) (Checkout)
- /utf8/plugins/tag/comps/contenido/tag/index.html (Diff) (Checkout)
- /utf8/plugins/tag/config.proto (Diff) (Checkout)
- /utf8/plugins/tag/lib
- /utf8/plugins/tag/lib/tag
- /utf8/plugins/tag/lib/tag/Apache.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/Cloud.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/Init.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/Keeper.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/SQL
- /utf8/plugins/tag/lib/tag/SQL/TagsCloudTable.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/SQL/TagsTable.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/Section.pm (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/State.pm.proto (Diff) (Checkout)
- /utf8/plugins/tag/lib/tag/Tag.pm (Diff) (Checkout)
- /utf8/plugins/tag/sql
- /utf8/plugins/tag/sql/TOAST
- /utf8/plugins/tag/sql/TOAST/tags.sql (Diff) (Checkout)
- /utf8/plugins/tag/sql/TOAST/tags_cloud.sql (Diff) (Checkout)
Legend:
- Added
- Removed
- Modified
-
utf8/plugins/tag/comps/contenido/components/inputs/tagset.msn
1 % if ( $object->id ) { 2 <script type="text/javascript"> 3 <!-- 4 function <% $name %>_onAddTag(tag) { 5 $.ajax({ 6 'url' : '/contenido/tag/ajax/manage.html', 7 'type' : 'GET', 8 'data' : { 'action' : 'add', 'id' : <% $object->id %>, 'class' : '<% $object->class %>', 'tag' : tag }, 9 'dataType' : 'json', 10 'success' : function( data ) { 11 if ( data.fallback ) { 12 alert('Не могу добавить тег ' + tag + '. Откат'); 13 $('#<% $name%>_text').removeTag(tag); 14 } 15 } 16 }); 17 } 18 19 function <% $name %>_onRemoveTag(tag) { 20 $.ajax({ 21 'url' : '/contenido/tag/ajax/manage.html', 22 'type' : 'GET', 23 'data' : { 'action' : 'remove', 'id' : <% $object->id %>, 'class' : '<% $object->class %>', 'tag' : tag }, 24 'dataType' : 'json', 25 'success' : function( data ) { 26 if ( data.fallback ) { 27 alert('Не могу удалить тег ' + tag + '. Откат'); 28 $('#<% $name%>_text').addTag(tag); 29 } 30 } 31 }); 32 } 33 34 $(document).ready(function() { 35 36 $('#<% $name %>_text').tagsInput({ 37 'width' : 'auto', 38 'height' : '60px', 39 'minChars' : 3, 40 'autocomplete_url' : '/contenido/tag/ajax/search.html?id=<% $object->id %>&class=<% $object->class %>', 41 'onAddTag' : <% $name %>_onAddTag, 42 'onRemoveTag' : <% $name %>_onRemoveTag, 43 'defaultText' : 'Добавьте тег' 44 }); 45 46 % if ( @tags ) { 47 $('#<% $name %>_text').importTags('<% join(',', map { $_->name } @tags) %>'); 48 % } 49 50 }); 51 //--> 52 </script> 53 <div style="width:95%;"> 54 <input type="text" name="<% $name %>" id="<% $name %>_text" placeholder="Tags" class="tm-input"/> 55 </div> 56 % } else { 57 <div style="width:95%; padding:10px; border:1px solid green; background:#f0fff0"> 58 <div style="color:green">Ввод тегов возможен только после сохранения документа</div> 59 </div> 60 % } 61 <%once> 62 63 use Data::Recursive::Encode; 64 use JSON::XS; 65 my $json = JSON::XS->new->utf8; 66 67 </%once> 68 <%args> 69 70 $object 71 $name => undef 72 $check => undef 73 $prop => {} 74 75 </%args> 76 <%init> 77 78 my @tags; 79 if ( $object->id ) { 80 @tags = $keeper->get_documents( 81 class => 'tag::Tag', 82 lclass => 'tag::Cloud', 83 ldest => $object->id, 84 ldestclass => $object->class, 85 ); 86 } 87 my $value; 88 unless ( $prop->{virtual} ) { 89 if ( ref $object->$name ) { 90 $value = $object->$name; 91 } elsif ( $object->$name ) { 92 $value = Data::Recursive::Encode->encode_utf8( $json->decode( $object->$name ) ); 93 } 94 } 95 96 </%init> -
utf8/plugins/tag/comps/contenido/components/outputs/tagset.msn
1 <%once> 2 3 use JSON::XS; 4 my $json = JSON::XS->new; 5 6 </%once> 7 <%args> 8 9 $object => undef 10 $name => undef 11 $SETS => undef 12 13 </%args> 14 <%init> 15 16 return undef unless ref $SETS; 17 return undef unless $name; 18 19 return undef if $SETS->{$name} eq ''; 20 21 warn "\nTAG Store:\n" if $DEBUG; 22 warn Dumper $SETS->{$name} if $DEBUG; 23 24 my ($prop) = grep { $_->{attr} eq $name } $object->structure; 25 return undef if exists $prop->{virtual} && $prop->{virtual}; 26 my $class = $object->class; 27 my $is_extra = grep { ref $_ && $_->{attr} eq $name } $class->extra_properties ? 1 : 0; 28 29 my $result; 30 my @tags; 31 if ( $object->id ) { 32 @tags = $keeper->get_documents( 33 class => 'tag::Tag', 34 lclass => 'tag::Cloud', 35 ldest => $object->id, 36 ldestclass => $object->class, 37 ); 38 if ( @tags ) { 39 $result = []; 40 foreach my $tag ( @tags ) { 41 push @$result, { id => $tag->id, name => Encode::decode('utf-8', $tag->name) }; 42 } 43 } 44 unless ( $is_extra ) { 45 $result = Encode::encode('utf-8', $json->encode( $result )); 46 } 47 warn Dumper $result if $DEBUG; 48 return $result; 49 } else { 50 return undef; 51 } 52 53 54 </%init> -
utf8/plugins/tag/comps/contenido/tag/ajax/manage.html
1 <% $json %> 2 <%once> 3 4 use JSON::XS; 5 6 </%once> 7 <%args> 8 9 $action => undef 10 $id => undef 11 $class => undef 12 $tag => undef 13 14 </%args> 15 <%init> 16 17 my %result; 18 warn Dumper \%ARGS; 19 my $doc = $keeper->get_document_by_id( $id, class => $class ); 20 21 if ( ref $doc && $ARGS{tag} ) { 22 my @links = $keeper->get_links( 23 class => 'tag::Cloud', 24 dest_id => $id, 25 dest_class => $class, 26 ); 27 my @tags; 28 if ( @links ) { 29 my %ids = map { $_->source_id => 1 } @links; 30 my @ids = keys %ids; 31 @tags = $keeper->get_documents( 32 id => \@ids, 33 class => 'tag::Tag', 34 ) if @ids; 35 } 36 my ($tobj) = $keeper->get_documents( 37 class => 'tag::Tag', 38 name => $ARGS{tag}, 39 ilike => 1, 40 limit => 1, 41 ); 42 if ( $action eq 'add' ) { 43 unless ( ref $tobj ) { 44 $tobj = tag::Tag->new( $keeper ); 45 $tobj->name( $ARGS{tag} ); 46 $tobj->status( 1 ); 47 $tobj->store; 48 } 49 my $exists = (grep { $_->id == $tobj->id } @tags) ? 1 : 0; 50 if ( ref $tobj && !$exists ) { 51 my $link = tag::Cloud->new( $keeper ); 52 $link->source_id( $tobj->id ); 53 $link->source_class( $tobj->class ); 54 $link->dest_id( $id ); 55 $link->dest_class( $class ); 56 $link->status( 1 ); 57 $link->store; 58 } else { 59 $result{fallback} = 1; 60 } 61 } elsif ( $action eq 'remove' ) { 62 if ( ref $tobj ) { 63 my @delete = grep { $_->source_id == $tobj->id } @links; 64 foreach my $obj ( @delete ) { 65 $obj->delete; 66 } 67 my $count = $keeper->get_links( 68 class => 'tag::Cloud', 69 count => 1, 70 source_id => $tobj->id, 71 ); 72 $tobj->delete unless $count; 73 } else { 74 $result{fallback} = 1; 75 } 76 } 77 } 78 $result{ok} = 1 unless exists $result{fallback}; 79 80 my $json = encode_json \%result; 81 82 </%init> -
utf8/plugins/tag/comps/contenido/tag/ajax/search.html
1 <% $json %> 2 <%once> 3 4 use JSON::XS; 5 6 </%once> 7 <%args> 8 9 $id => undef 10 $class => undef 11 $term => undef 12 13 </%args> 14 <%init> 15 16 warn Dumper \%ARGS if $DEBUG; 17 return if length( $ARGS{term} ) < 3; 18 19 my @links = $keeper->get_links( 20 class => 'tag::Cloud', 21 dest_class => $class, 22 dest_id => $id, 23 ); 24 my %ex = map { $_->source_id => 1 } @links; 25 my @ex = keys %ex; 26 27 my @tags = $keeper->get_documents( 28 @ex ? ( excludes => \@ex ) : (), 29 class => 'tag::Tag', 30 status => 1, 31 name => '%'.$ARGS{term}.'%', 32 ilike => 1, 33 order_by => 'name', 34 ); 35 return unless @tags; 36 37 my @result = map { {id => $_->id, label => Encode::decode('utf-8', $_->name), value => Encode::decode('utf-8', $_->name)} } @tags; 38 39 my $json = encode_json \@result; 40 41 </%init> -
utf8/plugins/tag/comps/contenido/tag/autohandler
1 <%init> 2 3 $r->content_type('text/html'); 4 $m->call_next(); 5 6 </%init> -
utf8/plugins/tag/comps/contenido/tag/components/title_inc.msn
1 <script language="javascript" type="text/javascript" src="/contenido/tag/i/js/jquery.tagsinput.js"></script> 2 <link href="/contenido/tag/i/js/jquery.tagsinput.css" rel="stylesheet" type="text/css"> -
utf8/plugins/tag/comps/contenido/tag/dhandler
1 <& $call, %ARGS &> 2 <%init> 3 4 my $call; 5 if ( $r->uri eq '/contenido/tag/' ) { 6 $call = 'index.html'; 7 } else { 8 &abort404; 9 } 10 11 </%init> -
utf8/plugins/tag/comps/contenido/tag/i/js/jquery.tagsinput.css
1 div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;} 2 div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;} 3 div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; } 4 div.tagsinput input { width:100px; margin:0px; font-family: helvetica; font-size: 13px; border:1px solid transparent; padding:5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; } 5 div.tagsinput div { display:block; float: left; } 6 .tags_clear { clear: both; width: 100%; height: 0px; } 7 .not_valid {background: #FBD8DB !important; color: #90111A !important;} -
utf8/plugins/tag/comps/contenido/tag/i/js/jquery.tagsinput.js
1 /* 2 3 jQuery Tags Input Plugin 1.3.3 4 5 Copyright (c) 2011 XOXCO, Inc 6 7 Documentation for this plugin lives here: 8 http://xoxco.com/clickable/jquery-tags-input 9 10 Licensed under the MIT license: 11 http://www.opensource.org/licenses/mit-license.php 12 13 ben@xoxco.com 14 15 */ 16 17 (function($) { 18 19 var delimiter = new Array(); 20 var tags_callbacks = new Array(); 21 $.fn.doAutosize = function(o){ 22 var minWidth = $(this).data('minwidth'), 23 maxWidth = $(this).data('maxwidth'), 24 val = '', 25 input = $(this), 26 testSubject = $('#'+$(this).data('tester_id')); 27 28 if (val === (val = input.val())) {return;} 29 30 // Enter new content into testSubject 31 var escaped = val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>'); 32 testSubject.html(escaped); 33 // Calculate new width + whether to change 34 var testerWidth = testSubject.width(), 35 newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth, 36 currentWidth = input.width(), 37 isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth) 38 || (newWidth > minWidth && newWidth < maxWidth); 39 40 // Animate width 41 if (isValidWidthChange) { 42 input.width(newWidth); 43 } 44 45 46 }; 47 $.fn.resetAutosize = function(options){ 48 // alert(JSON.stringify(options)); 49 var minWidth = $(this).data('minwidth') || options.minInputWidth || $(this).width(), 50 maxWidth = $(this).data('maxwidth') || options.maxInputWidth || ($(this).closest('.tagsinput').width() - options.inputPadding), 51 val = '', 52 input = $(this), 53 testSubject = $('<tester/>').css({ 54 position: 'absolute', 55 top: -9999, 56 left: -9999, 57 width: 'auto', 58 fontSize: input.css('fontSize'), 59 fontFamily: input.css('fontFamily'), 60 fontWeight: input.css('fontWeight'), 61 letterSpacing: input.css('letterSpacing'), 62 whiteSpace: 'nowrap' 63 }), 64 testerId = $(this).attr('id')+'_autosize_tester'; 65 if(! $('#'+testerId).length > 0){ 66 testSubject.attr('id', testerId); 67 testSubject.appendTo('body'); 68 } 69 70 input.data('minwidth', minWidth); 71 input.data('maxwidth', maxWidth); 72 input.data('tester_id', testerId); 73 input.css('width', minWidth); 74 }; 75 76 $.fn.addTag = function(value,options) { 77 options = jQuery.extend({focus:false,callback:true},options); 78 this.each(function() { 79 var id = $(this).attr('id'); 80 81 var tagslist = $(this).val().split(delimiter[id]); 82 if (tagslist[0] == '') { 83 tagslist = new Array(); 84 } 85 86 value = jQuery.trim(value); 87 88 if (options.unique) { 89 var skipTag = $(this).tagExist(value); 90 if(skipTag == true) { 91 //Marks fake input as not_valid to let styling it 92 $('#'+id+'_tag').addClass('not_valid'); 93 } 94 } else { 95 var skipTag = false; 96 } 97 98 if (value !='' && skipTag != true) { 99 $('<span>').addClass('tag').append( 100 $('<span>').text(value).append(' '), 101 $('<a>', { 102 href : '#', 103 title : 'Removing tag', 104 text : 'x' 105 }).click(function () { 106 return $('#' + id).removeTag(escape(value)); 107 }) 108 ).insertBefore('#' + id + '_addTag'); 109 110 tagslist.push(value); 111 112 $('#'+id+'_tag').val(''); 113 if (options.focus) { 114 $('#'+id+'_tag').focus(); 115 } else { 116 $('#'+id+'_tag').blur(); 117 } 118 119 $.fn.tagsInput.updateTagsField(this,tagslist); 120 121 if (options.callback && tags_callbacks[id] && tags_callbacks[id]['onAddTag']) { 122 var f = tags_callbacks[id]['onAddTag']; 123 f.call(this, value); 124 } 125 if(tags_callbacks[id] && tags_callbacks[id]['onChange']) 126 { 127 var i = tagslist.length; 128 var f = tags_callbacks[id]['onChange']; 129 f.call(this, $(this), tagslist[i-1]); 130 } 131 } 132 133 }); 134 135 return false; 136 }; 137 138 $.fn.removeTag = function(value) { 139 value = unescape(value); 140 this.each(function() { 141 var id = $(this).attr('id'); 142 143 var old = $(this).val().split(delimiter[id]); 144 145 $('#'+id+'_tagsinput .tag').remove(); 146 str = ''; 147 for (i=0; i< old.length; i++) { 148 if (old[i]!=value) { 149 str = str + delimiter[id] +old[i]; 150 } 151 } 152 153 $.fn.tagsInput.importTags(this,str); 154 155 if (tags_callbacks[id] && tags_callbacks[id]['onRemoveTag']) { 156 var f = tags_callbacks[id]['onRemoveTag']; 157 f.call(this, value); 158 } 159 }); 160 161 return false; 162 }; 163 164 $.fn.tagExist = function(val) { 165 var id = $(this).attr('id'); 166 var tagslist = $(this).val().split(delimiter[id]); 167 return (jQuery.inArray(val, tagslist) >= 0); //true when tag exists, false when not 168 }; 169 170 // clear all existing tags and import new ones from a string 171 $.fn.importTags = function(str) { 172 id = $(this).attr('id'); 173 $('#'+id+'_tagsinput .tag').remove(); 174 $.fn.tagsInput.importTags(this,str); 175 } 176 177 $.fn.tagsInput = function(options) { 178 var settings = jQuery.extend({ 179 interactive:true, 180 defaultText:'add a tag', 181 minChars:0, 182 width:'300px', 183 height:'100px', 184 autocomplete: {selectFirst: false }, 185 'hide':true, 186 'delimiter':',', 187 'unique':true, 188 removeWithBackspace:true, 189 placeholderColor:'#666666', 190 autosize: true, 191 comfortZone: 20, 192 inputPadding: 6*2 193 },options); 194 195 this.each(function() { 196 if (settings.hide) { 197 $(this).hide(); 198 } 199 var id = $(this).attr('id'); 200 if (!id || delimiter[$(this).attr('id')]) { 201 id = $(this).attr('id', 'tags' + new Date().getTime()).attr('id'); 202 } 203 204 var data = jQuery.extend({ 205 pid:id, 206 real_input: '#'+id, 207 holder: '#'+id+'_tagsinput', 208 input_wrapper: '#'+id+'_addTag', 209 fake_input: '#'+id+'_tag' 210 },settings); 211 212 delimiter[id] = data.delimiter; 213 214 if (settings.onAddTag || settings.onRemoveTag || settings.onChange) { 215 tags_callbacks[id] = new Array(); 216 tags_callbacks[id]['onAddTag'] = settings.onAddTag; 217 tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag; 218 tags_callbacks[id]['onChange'] = settings.onChange; 219 } 220 221 var markup = '<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag">'; 222 223 if (settings.interactive) { 224 markup = markup + '<input id="'+id+'_tag" value="" data-default="'+settings.defaultText+'" />'; 225 } 226 227 markup = markup + '</div><div class="tags_clear"></div></div>'; 228 229 $(markup).insertAfter(this); 230 231 $(data.holder).css('width',settings.width); 232 $(data.holder).css('min-height',settings.height); 233 $(data.holder).css('height','100%'); 234 235 if ($(data.real_input).val()!='') { 236 $.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val()); 237 } 238 if (settings.interactive) { 239 $(data.fake_input).val($(data.fake_input).attr('data-default')); 240 $(data.fake_input).css('color',settings.placeholderColor); 241 $(data.fake_input).resetAutosize(settings); 242 243 $(data.holder).bind('click',data,function(event) { 244 $(event.data.fake_input).focus(); 245 }); 246 247 $(data.fake_input).bind('focus',data,function(event) { 248 if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('data-default')) { 249 $(event.data.fake_input).val(''); 250 } 251 $(event.data.fake_input).css('color','#000000'); 252 }); 253 254 if (settings.autocomplete_url != undefined) { 255 autocomplete_options = {source: settings.autocomplete_url}; 256 for (attrname in settings.autocomplete) { 257 autocomplete_options[attrname] = settings.autocomplete[attrname]; 258 } 259 260 if (jQuery.Autocompleter !== undefined) { 261 $(data.fake_input).autocomplete(settings.autocomplete_url, settings.autocomplete); 262 $(data.fake_input).bind('result',data,function(event,data,formatted) { 263 if (data) { 264 $('#'+id).addTag(data[0] + "",{focus:true,unique:(settings.unique)}); 265 } 266 }); 267 } else if (jQuery.ui.autocomplete !== undefined) { 268 $(data.fake_input).autocomplete(autocomplete_options); 269 $(data.fake_input).bind('autocompleteselect',data,function(event,ui) { 270 $(event.data.real_input).addTag(ui.item.value,{focus:true,unique:(settings.unique)}); 271 return false; 272 }); 273 } 274 275 276 } else { 277 // if a user tabs out of the field, create a new tag 278 // this is only available if autocomplete is not used. 279 $(data.fake_input).bind('blur',data,function(event) { 280 var d = $(this).attr('data-default'); 281 if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) { 282 if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) ) 283 $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)}); 284 } else { 285 $(event.data.fake_input).val($(event.data.fake_input).attr('data-default')); 286 $(event.data.fake_input).css('color',settings.placeholderColor); 287 } 288 return false; 289 }); 290 291 } 292 // if user types a comma, create a new tag 293 $(data.fake_input).bind('keypress',data,function(event) { 294 if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13 ) { 295 event.preventDefault(); 296 if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) ) 297 $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)}); 298 $(event.data.fake_input).resetAutosize(settings); 299 return false; 300 } else if (event.data.autosize) { 301 $(event.data.fake_input).doAutosize(settings); 302 303 } 304 }); 305 //Delete last tag on backspace 306 data.removeWithBackspace && $(data.fake_input).bind('keydown', function(event) 307 { 308 if(event.keyCode == 8 && $(this).val() == '') 309 { 310 event.preventDefault(); 311 var last_tag = $(this).closest('.tagsinput').find('.tag:last').text(); 312 var id = $(this).attr('id').replace(/_tag$/, ''); 313 last_tag = last_tag.replace(/[\s]+x$/, ''); 314 $('#' + id).removeTag(escape(last_tag)); 315 $(this).trigger('focus'); 316 } 317 }); 318 $(data.fake_input).blur(); 319 320 //Removes the not_valid class when user changes the value of the fake input 321 if(data.unique) { 322 $(data.fake_input).keydown(function(event){ 323 if(event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)) { 324 $(this).removeClass('not_valid'); 325 } 326 }); 327 } 328 } // if settings.interactive 329 }); 330 331 return this; 332 333 }; 334 335 $.fn.tagsInput.updateTagsField = function(obj,tagslist) { 336 var id = $(obj).attr('id'); 337 $(obj).val(tagslist.join(delimiter[id])); 338 }; 339 340 $.fn.tagsInput.importTags = function(obj,val) { 341 $(obj).val(''); 342 var id = $(obj).attr('id'); 343 var tags = val.split(delimiter[id]); 344 for (i=0; i<tags.length; i++) { 345 $(obj).addTag(tags[i],{focus:false,callback:false}); 346 } 347 if(tags_callbacks[id] && tags_callbacks[id]['onChange']) 348 { 349 var f = tags_callbacks[id]['onChange']; 350 f.call(obj, obj, tags[i]); 351 } 352 }; 353 354 })(jQuery); -
utf8/plugins/tag/comps/contenido/tag/i/js/jquery.tagsinput.min.js
1 (function(a){var b=new Array;var c=new Array;a.fn.doAutosize=function(b){var c=a(this).data("minwidth"),d=a(this).data("maxwidth"),e="",f=a(this),g=a("#"+a(this).data("tester_id"));if(e===(e=f.val())){return}var h=e.replace(/&/g,"&").replace(/\s/g," ").replace(/</g,"<").replace(/>/g,">");g.html(h);var i=g.width(),j=i+b.comfortZone>=c?i+b.comfortZone:c,k=f.width(),l=j<k&&j>=c||j>c&&j<d;if(l){f.width(j)}};a.fn.resetAutosize=function(b){var c=a(this).data("minwidth")||b.minInputWidth||a(this).width(),d=a(this).data("maxwidth")||b.maxInputWidth||a(this).closest(".tagsinput").width()-b.inputPadding,e="",f=a(this),g=a("<tester/>").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:f.css("fontSize"),fontFamily:f.css("fontFamily"),fontWeight:f.css("fontWeight"),letterSpacing:f.css("letterSpacing"),whiteSpace:"nowrap"}),h=a(this).attr("id")+"_autosize_tester";if(!a("#"+h).length>0){g.attr("id",h);g.appendTo("body")}f.data("minwidth",c);f.data("maxwidth",d);f.data("tester_id",h);f.css("width",c)};a.fn.addTag=function(d,e){e=jQuery.extend({focus:false,callback:true},e);this.each(function(){var f=a(this).attr("id");var g=a(this).val().split(b[f]);if(g[0]==""){g=new Array}d=jQuery.trim(d);if(e.unique){var h=a(g).tagExist(d);if(h==true){a("#"+f+"_tag").addClass("not_valid")}}else{var h=false}if(d!=""&&h!=true){a("<span>").addClass("tag").append(a("<span>").text(d).append(" "),a("<a>",{href:"#",title:"Removing tag",text:"x"}).click(function(){return a("#"+f).removeTag(escape(d))})).insertBefore("#"+f+"_addTag");g.push(d);a("#"+f+"_tag").val("");if(e.focus){a("#"+f+"_tag").focus()}else{a("#"+f+"_tag").blur()}a.fn.tagsInput.updateTagsField(this,g);if(e.callback&&c[f]&&c[f]["onAddTag"]){var i=c[f]["onAddTag"];i.call(this,d)}if(c[f]&&c[f]["onChange"]){var j=g.length;var i=c[f]["onChange"];i.call(this,a(this),g[j-1])}}});return false};a.fn.removeTag=function(d){d=unescape(d);this.each(function(){var e=a(this).attr("id");var f=a(this).val().split(b[e]);a("#"+e+"_tagsinput .tag").remove();str="";for(i=0;i<f.length;i++){if(f[i]!=d){str=str+b[e]+f[i]}}a.fn.tagsInput.importTags(this,str);if(c[e]&&c[e]["onRemoveTag"]){var g=c[e]["onRemoveTag"];g.call(this,d)}});return false};a.fn.tagExist=function(b){return jQuery.inArray(b,a(this))>=0};a.fn.importTags=function(b){id=a(this).attr("id");a("#"+id+"_tagsinput .tag").remove();a.fn.tagsInput.importTags(this,b)};a.fn.tagsInput=function(d){var e=jQuery.extend({interactive:true,defaultText:"add a tag",minChars:0,width:"300px",height:"100px",autocomplete:{selectFirst:false},hide:true,delimiter:",",unique:true,removeWithBackspace:true,placeholderColor:"#666666",autosize:true,comfortZone:20,inputPadding:6*2},d);this.each(function(){if(e.hide){a(this).hide()}var d=a(this).attr("id");if(!d||b[a(this).attr("id")]){d=a(this).attr("id","tags"+(new Date).getTime()).attr("id")}var f=jQuery.extend({pid:d,real_input:"#"+d,holder:"#"+d+"_tagsinput",input_wrapper:"#"+d+"_addTag",fake_input:"#"+d+"_tag"},e);b[d]=f.delimiter;if(e.onAddTag||e.onRemoveTag||e.onChange){c[d]=new Array;c[d]["onAddTag"]=e.onAddTag;c[d]["onRemoveTag"]=e.onRemoveTag;c[d]["onChange"]=e.onChange}var g='<div id="'+d+'_tagsinput" class="tagsinput"><div id="'+d+'_addTag">';if(e.interactive){g=g+'<input id="'+d+'_tag" value="" data-default="'+e.defaultText+'" />'}g=g+'</div><div class="tags_clear"></div></div>';a(g).insertAfter(this);a(f.holder).css("width",e.width);a(f.holder).css("height",e.height);if(a(f.real_input).val()!=""){a.fn.tagsInput.importTags(a(f.real_input),a(f.real_input).val())}if(e.interactive){a(f.fake_input).val(a(f.fake_input).attr("data-default"));a(f.fake_input).css("color",e.placeholderColor);a(f.fake_input).resetAutosize(e);a(f.holder).bind("click",f,function(b){a(b.data.fake_input).focus()});a(f.fake_input).bind("focus",f,function(b){if(a(b.data.fake_input).val()==a(b.data.fake_input).attr("data-default")){a(b.data.fake_input).val("")}a(b.data.fake_input).css("color","#000000")});if(e.autocomplete_url!=undefined){autocomplete_options={source:e.autocomplete_url};for(attrname in e.autocomplete){autocomplete_options[attrname]=e.autocomplete[attrname]}if(jQuery.Autocompleter!==undefined){a(f.fake_input).autocomplete(e.autocomplete_url,e.autocomplete);a(f.fake_input).bind("result",f,function(b,c,f){if(c){a("#"+d).addTag(c[0]+"",{focus:true,unique:e.unique})}})}else if(jQuery.ui.autocomplete!==undefined){a(f.fake_input).autocomplete(autocomplete_options);a(f.fake_input).bind("autocompleteselect",f,function(b,c){a(b.data.real_input).addTag(c.item.value,{focus:true,unique:e.unique});return false})}}else{a(f.fake_input).bind("blur",f,function(b){var c=a(this).attr("data-default");if(a(b.data.fake_input).val()!=""&&a(b.data.fake_input).val()!=c){if(b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length))a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:true,unique:e.unique})}else{a(b.data.fake_input).val(a(b.data.fake_input).attr("data-default"));a(b.data.fake_input).css("color",e.placeholderColor)}return false})}a(f.fake_input).bind("keypress",f,function(b){if(b.which==b.data.delimiter.charCodeAt(0)||b.which==13){b.preventDefault();if(b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length))a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:true,unique:e.unique});a(b.data.fake_input).resetAutosize(e);return false}else if(b.data.autosize){a(b.data.fake_input).doAutosize(e)}});f.removeWithBackspace&&a(f.fake_input).bind("keydown",function(b){if(b.keyCode==8&&a(this).val()==""){b.preventDefault();var c=a(this).closest(".tagsinput").find(".tag:last").text();var d=a(this).attr("id").replace(/_tag$/,"");c=c.replace(/[\s]+x$/,"");a("#"+d).removeTag(escape(c));a(this).trigger("focus")}});a(f.fake_input).blur();if(f.unique){a(f.fake_input).keydown(function(b){if(b.keyCode==8||String.fromCharCode(b.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)){a(this).removeClass("not_valid")}})}}});return this};a.fn.tagsInput.updateTagsField=function(c,d){var e=a(c).attr("id");a(c).val(d.join(b[e]))};a.fn.tagsInput.importTags=function(d,e){a(d).val("");var f=a(d).attr("id");var g=e.split(b[f]);for(i=0;i<g.length;i++){a(d).addTag(g[i],{focus:false,callback:false})}if(c[f]&&c[f]["onChange"]){var h=c[f]["onChange"];h.call(d,d,g[i])}}})(jQuery); -
utf8/plugins/tag/comps/contenido/tag/index.html
1 <& "/contenido/components/header.msn" &> 2 <& "/contenido/components/naviline.msn" &> 3 4 <p>PLugin [tag]</p> 5 6 </body> 7 </html> -
utf8/plugins/tag/config.proto
1 ############################################################################# 2 # 3 # Параметры данного шаблона необходимо ВРУЧНУЮ добавить в config.mk проекта 4 # и привести в соответствие с требованиями проекта 5 # 6 ############################################################################# 7 8 PLUGINS += tag 9 10 TAG_DEST = <список через пробел классов тегируемых документов> 11 REWRITE += TAG_DEST -
utf8/plugins/tag/lib/tag/Apache.pm
1 package tag::Apache; 2 3 use strict; 4 use warnings 'all'; 5 6 use tag::State; 7 use Contenido::Globals; 8 9 10 sub child_init { 11 # встраиваем keeper плагина в keeper проекта 12 $keeper->{tag} = tag::Keeper->new($state->tag); 13 } 14 15 sub request_init { 16 } 17 18 sub child_exit { 19 } 20 21 1; -
utf8/plugins/tag/lib/tag/Cloud.pm
1 package tag::Cloud; 2 3 use base 'Contenido::Link'; 4 use Contenido::Globals; 5 use Data::Recursive::Encode; 6 use Data::Dumper; 7 use JSON::XS; 8 9 sub class_name 10 { 11 return 'Тег к объекту'; 12 } 13 14 sub class_description 15 { 16 return 'Организация облака тегов'; 17 } 18 19 sub extra_properties 20 { 21 return ( 22 ); 23 } 24 25 sub available_sources 26 { 27 return [ qw(tag::Tag) ]; 28 } 29 30 sub available_destinations 31 { 32 return $state->{tag}->tag_destinations; 33 } 34 35 sub class_table 36 { 37 return 'tag::SQL::TagsCloudTable'; 38 } 39 40 sub pre_store 41 { 42 my $self = shift; 43 44 return 1; 45 } 46 47 sub post_store 48 { 49 my $self = shift; 50 warn "Store tag cloud element\n" if $DEBUG; 51 my $object = $keeper->get_document_by_id($self->dest_id, class => $self->dest_class ) if $self->dest_id && $self->dest_class; 52 my $tag = $self->keeper->get_document_by_id($self->source_id, class => $self->source_class ) if $self->source_id && $self->source_class; 53 if ( ref $object && ref $tag ) { 54 my ($prop) = grep { $_->{type} eq 'tagset' } $object->structure; 55 my $class = $object->class; 56 my $is_extra = grep { ref $_ && $_->{attr} eq $name } $class->extra_properties ? 1 : 0; 57 if ( ref $prop && !(exists $prop->{virtual} && $prop->{virtual}) ) { 58 my $name = $prop->{attr}; 59 my $struct; 60 if ( ref $object->$name ) { 61 $struct = $object->$name; 62 } elsif ( $object->$name ) { 63 $struct = JSON::XS->new->utf8->decode( $object->$name ); 64 } 65 if ( ref $struct eq 'ARRAY' && @$struct && !(grep { $_->{id} == $tag->id } @$struct) ) { 66 push @$struct, { id => $tag->id, name => Encode::decode('utf-8', $tag->name) }; 67 unless ( $is_extra ) { 68 $struct = Encode::encode('utf-8', JSON::XS->new->encode( $struct )); 69 } 70 $object->$name( $struct ); 71 $object->store; 72 } 73 } 74 } else { 75 warn "Tag Cloud update error: cloud_element_id=".$self->id.", no source or destination available\n"; 76 } 77 } 78 79 sub post_delete 80 { 81 my $self = shift; 82 my $object = $keeper->get_document_by_id($self->dest_id, class => $self->dest_class ) if $self->dest_id && $self->dest_class; 83 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 )); 100 } 101 $object->$name( $struct ); 102 $object->store; 103 } 104 } 105 } else { 106 warn "Tag Cloud delete error: cloud_element_id=".$self->id.", no source or destination available\n"; 107 } 108 } 109 110 1; -
utf8/plugins/tag/lib/tag/Init.pm
1 package tag::Init; 2 3 use strict; 4 use warnings 'all'; 5 6 use Contenido::Globals; 7 use tag::Apache; 8 use tag::Keeper; 9 use tag::Section; 10 use tag::Tag; 11 use tag::Cloud; 12 use tag::SQL::TagsCloudTable; 13 use tag::SQL::TagsTable; 14 15 # загрузка всех необходимых плагину классов 16 # tag::SQL::SomeTable 17 # tag::SomeClass 18 Contenido::Init::load_classes(qw( 19 tag::Section 20 tag::Tag 21 tag::Cloud 22 tag::SQL::TagsCloudTable 23 tag::SQL::TagsTable 24 )); 25 26 sub init { 27 push @{ $state->{'available_documents'} }, 28 qw ( 29 tag::Tag 30 ); 31 push @{ $state->{'available_sections'} }, 32 qw ( 33 tag::Section 34 ); 35 push @{ $state->{'available_links'} }, 36 qw ( 37 tag::Cloud 38 ); 39 0; 40 } 41 42 1; -
utf8/plugins/tag/lib/tag/Keeper.pm
1 package tag::Keeper; 2 3 use strict; 4 use warnings 'all'; 5 use base qw(Contenido::Keeper); 6 7 8 use Contenido::Globals; 9 10 11 1; -
utf8/plugins/tag/lib/tag/Section.pm
1 package tag::Section; 2 3 use base 'Contenido::Section'; 4 5 sub extra_properties 6 { 7 return ( 8 { 'attr' => 'default_document_class', 'default' => 'tag::Tag' }, 9 ) 10 } 11 12 sub class_name 13 { 14 return 'Секция тегов'; 15 } 16 17 sub class_description 18 { 19 return 'Секция тегов'; 20 } 21 22 1; -
utf8/plugins/tag/lib/tag/SQL/TagsCloudTable.pm
1 package tag::SQL::TagsCloudTable; 2 3 use strict; 4 use base 'SQL::LinkTable'; 5 6 sub db_table 7 { 8 return 'tags_cloud'; 9 } 10 11 # ---------------------------------------------------------------------------- 12 # Свойства храним в массивах, потому что порядок важен! 13 # Это общие свойства - одинаковые для всех документов. 14 # 15 # attr - обязательный параметр, название атрибута; 16 # type - тип аттрибута, требуется для отображдения; 17 # rusname - русское название, опять же требуется для отображения; 18 # hidden - равен 1, когда 19 # readonly - инициализации при записи только без изменения в дальнейшем 20 # db_field - поле в таблице 21 # default - значение по умолчанию (поле всегда имеет это значение) 22 # ---------------------------------------------------------------------------- 23 sub required_properties 24 { 25 my $self = shift; 26 27 my @parent_properties = $self->SUPER::required_properties; 28 return ( 29 @parent_properties, 30 ); 31 } 32 33 ########### FILTERS DESCRIPTION #################################################################################### 34 35 1; 36 -
utf8/plugins/tag/lib/tag/SQL/TagsTable.pm
1 package tag::SQL::TagsTable; 2 3 use strict; 4 use base 'SQL::DocumentTable'; 5 6 sub db_table 7 { 8 return '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 _prev_to_filter 20 _next_to_filter 21 _s_filter 22 23 _excludes_filter 24 _link_filter 25 _alias_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 my $self = shift; 45 46 my @parent_properties = grep { $_->{attr} ne 'dtime' && $_->{attr} ne 'sections' } $self->SUPER::required_properties; 47 return ( 48 @parent_properties, 49 { 50 'attr' => 'alias', 51 'type' => 'string', 52 'rusname' => 'Алиас', 53 'column' => 3, 54 'db_field' => 'alias', 55 'db_type' => 'text', 56 }, 57 { # Подменяем секцию 58 'attr' => 'sections', 59 'type' => 'parent', 60 'rusname' => 'Родительская секция', 61 'db_field' => 'sections', 62 'db_type' => 'integer', 63 }, 64 ); 65 } 66 67 ########### FILTERS DESCRIPTION #################################################################################### 68 sub _s_filter { 69 my ($self,%opts)=@_; 70 return undef unless ( exists $opts{s} ); 71 return &SQL::Common::_generic_int_filter('d.sections', $opts{s}); 72 } 73 74 sub _alias_filter { 75 my ($self,%opts)=@_; 76 return undef unless ( exists $opts{alias} ); 77 return &SQL::Common::_generic_text_filter('d.alias', $opts{alias}); 78 } 79 80 sub _link_filter { 81 my ($self,%opts)=@_; 82 83 my @wheres=(); 84 my @binds=(); 85 86 # Связь определенного класса 87 if (exists($opts{lclass})) { 88 my ($where, $values) = SQL::Common::_generic_text_filter('l.class', $opts{lclass}); 89 push (@wheres, $where); 90 push (@binds, ref($values) ? @$values:$values) if (defined $values); 91 } 92 93 my $lclass = $opts{lclass} || 'Contenido::Link'; 94 my $link_table = $lclass->_get_table->db_table(); 95 96 # Ограничение по статусу связи 97 if ( exists $opts{lstatus} ) { 98 my ($where, $values) = SQL::Common::_generic_int_filter('l.status', $opts{lstatus}); 99 push (@wheres, $where); 100 push (@binds, ref($values) ? @$values:$values) if (defined $values); 101 } 102 103 # Связь с определенным документ(ом/тами) по цели линка 104 if ( exists $opts{ldest} ) { 105 my ($where, $values) = SQL::Common::_generic_int_filter('l.dest_id', $opts{ldest}); 106 push (@wheres, $where); 107 push (@binds, ref($values) ? @$values:$values) if (defined $values); 108 if ($self->_single_class) { 109 return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id"); 110 } elsif ( exists $opts{ldestclass} ) { 111 my ($where, $values) = SQL::Common::_generic_text_filter('l.dest_class', $opts{ldestclass}); 112 push (@wheres, $where); 113 push (@binds, ref($values) ? @$values:$values) if (defined $values); 114 115 return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id and l.source_class=d.class"); 116 } else { 117 return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id and l.source_class=d.class"); 118 } 119 } 120 121 # Связь с определенным документ(ом/тами) по источнику линка 122 if ( exists $opts{lsource} ) { 123 my ($where, $values) = SQL::Common::_generic_int_filter('l.source_id', $opts{lsource}); 124 push (@wheres, $where); 125 push (@binds, ref($values) ? @$values:$values) if (defined $values); 126 if ($self->_single_class) { 127 return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id"); 128 } elsif ( exists $opts{lsourceclass} ) { 129 my ($where, $values) = SQL::Common::_generic_text_filter('l.source_class', $opts{lsourceclass}); 130 push (@wheres, $where); 131 push (@binds, ref($values) ? @$values:$values) if (defined $values); 132 133 return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id and l.dest_class=d.class"); 134 } else { 135 return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id and l.dest_class=d.class"); 136 } 137 } 138 139 return (undef); 140 } 141 142 143 1; -
utf8/plugins/tag/lib/tag/State.pm.proto
1 package tag::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->{project} = '@PROJECT@'; 16 $self->{debug} = (lc('@DEBUG@') eq 'yes'); 17 $self->{contenido_notab} = 0; 18 $self->{tab_name} = 'Теги'; 19 $self->{project_name} = '@PROJECT_NAME@'; 20 $self->{default_expire} = '@DEFAULT_EXPIRE@' || 300; 21 $self->{default_object_expire} = '@DEFAULT_OBJECT_EXPIRE@' || 600; 22 23 # зашитая конфигурация плагина 24 $self->{db_type} = 'none'; ### For REAL database use 'remote' 25 $self->{db_keepalive} = 0; 26 $self->{db_host} = ''; 27 $self->{db_name} = ''; 28 $self->{db_user} = ''; 29 $self->{db_password} = ''; 30 $self->{db_port} = ''; 31 $self->{store_method} = 'toast'; 32 $self->{cascade} = 1; 33 $self->{db_prepare} = 0; 34 35 $self->{memcached_enable} = lc( '@MEMCACHED_ENABLE@' ) eq 'yes' ? 1 : 0; 36 $self->{memcached_backend} = '@MEMCACHED_BACKEND@'; 37 $self->{memcached_select_timeout} = '@MEMCACHED_SELECT_TIMEOUT@' || 0.2; 38 $self->{memcached_servers} = [qw(@MEMCACHED_SERVERS@)]; 39 $self->{memcached_enable_compress} = lc( '@MEMCACHED_ENABLE_COMPRESS@' ) eq 'yes' ? 1 : 0; 40 $self->{memcached_delayed} = lc('@MEMCACHED_DELAYED@') eq 'yes' ? 1 : 0; 41 $self->{memcached_set_mode} = lc('@MEMCACHED_SET_MODE@') eq 'add' ? 'add' : 'set'; 42 $self->{memcached_busy_lock} = 60; 43 $self->{memcached_namespace} = lc( $self->{'project'} ).'|plugin_tag|'; 44 45 $self->{serialize_with} = 'json'; ### or 'dumper' 46 47 # not implemented really (core compatibility) 48 $self->{data_directory} = ''; 49 $self->{images_directory} = ''; 50 $self->{binary_directory} = ''; 51 $self->{preview} = ''; 52 53 $self->{tag_destinations} = [qw(@TAG_DEST@)]; 54 55 $self->_init_(); 56 $self; 57 } 58 59 sub info { 60 my $self = shift; 61 return unless ref $self; 62 63 for (sort keys %{$self->{attributes}}) { 64 my $la = length $_; 65 warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n"; 66 } 67 } 68 69 sub _init_ { 70 my $self = shift; 71 72 # зашитая конфигурация плагина 73 $self->{attributes}->{$_} = 'SCALAR' for qw( 74 debug 75 project 76 tab_name 77 78 db_type 79 db_keepalive 80 db_host 81 db_port 82 db_name 83 db_user 84 db_password 85 store_method 86 cascade 87 db_prepare 88 db_client_encoding 89 90 memcached_enable 91 memcached_servers 92 memcached_select_timeout 93 memcached_backend 94 memcached_enable_compress 95 memcached_set_mode 96 memcached_object_expire 97 memcached_busy_lock 98 memcached_delayed 99 memcached_namespace 100 101 binary_directory 102 data_directory 103 images_directory 104 preview 105 106 tag_destinations 107 ); 108 } 109 110 sub AUTOLOAD { 111 my $self = shift; 112 my $attribute = $AUTOLOAD; 113 114 $attribute =~ s/.*:://; 115 return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY 116 117 if (!exists $self->{attributes}->{$attribute}) { 118 warn "Contenido Error (tag::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n"; 119 return; 120 } 121 122 $self->{$attribute} = shift @_ if $#_>=0; 123 $self->{$attribute}; 124 } 125 126 1; -
utf8/plugins/tag/lib/tag/Tag.pm
1 package tag::Tag; 2 3 use base 'Contenido::Document'; 4 use Contenido::Globals; 5 6 sub extra_properties 7 { 8 return ( 9 { 'attr' => 'name', 'rusname' => 'Название тега', shortname => 'Тег' }, 10 ) 11 } 12 13 14 sub class_name 15 { 16 return 'Тег'; 17 } 18 19 sub class_description 20 { 21 return 'Тег'; 22 } 23 24 sub class_table 25 { 26 return 'tag::SQL::TagsTable'; 27 } 28 29 sub search_fields { 30 return ('name'); 31 } 32 33 sub pre_store 34 { 35 my $self = shift; 36 37 my $default_section = $project->s_alias->{tags} if ref $project->s_alias eq 'HASH' && exists $project->s_alias->{tags}; 38 $self->sections( $default_section ) if $default_section; 39 40 return 1; 41 } 42 43 sub post_delete 44 { 45 my $self = shift; 46 47 my $links = $self->keeper->get_links( 48 class => 'tag::Cloud', 49 source_id => $self->id, 50 return_mode => 'array_ref', 51 ); 52 if ( ref $links eq 'ARRAY' && @$links ) { 53 foreach my $link ( @$links ) { 54 $link->delete; 55 } 56 } 57 } 58 59 1; -
utf8/plugins/tag/sql/TOAST/tags.sql
1 create table tags 2 ( 3 id integer not null primary key default nextval('public.documents_id_seq'::text), 4 ctime timestamp not null default now(), 5 mtime timestamp not null default now(), 6 class text not null, 7 status smallint not null default 0, 8 sections integer, 9 name text, 10 alias text, 11 data text 12 ); 13 create index tags_name on tags (name); 14 create index tags_alias on tags (alias) WHERE alias IS NOT NULL AND alias != ''; -
utf8/plugins/tag/sql/TOAST/tags_cloud.sql
1 create table tags_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 not null default 'tags::Tag', 10 dest_id integer not null, 11 dest_class text not null, 12 data text 13 ); 14 create index tags_cloud_source on tags_cloud (source_id); 15 create index tags_cloud_dest on tags_cloud (dest_id);