10 $Gruta::VERSION
= '2.3.3-dev';
11 $Gruta::VERSION_CODENAME
= '"Volterra"';
26 $Gruta::VERSION
. ' ' . $Gruta::VERSION_CODENAME
;
33 print STDERR
$self->{id
}, ' ', scalar(localtime), ': ', $msg, "\n";
39 my $story = shift; # Gruta::Data::Story
41 my $format = $story->get('format') || 'grutatxt';
43 if (my $rndr = $self->{renderers_h
}->{$format}) {
53 $self->{auth
} = shift; # Gruta::Data::User
64 $self->{session
} = shift; # Gruta::Data::Session
67 return $self->{session
};
78 $self->source->purge_old_sessions();
80 if (my $session = $self->source->session($sid)) {
81 $u = $session->source->user( $session->get('user_id') );
85 $self->session($session);
101 if (my $u = $self->source->user( $user_id )) {
103 # account expired? go!
104 if (my $xdate = $u->get('xdate')) {
105 if (Gruta
::Data
::today
() > $xdate) {
110 my $p = $u->get('password');
112 if (Gruta
::Data
::crypt($passwd, $p) eq $p) {
114 my $session = Gruta
::Data
::Session
->new(user_id
=> $user_id);
115 $self->source->insert_session($session);
117 # store user and session
119 $self->session($session);
121 # and return sid to signal a valid login
122 $sid = $session->get('id');
134 if (my $session = $self->session()) {
139 $self->session(undef);
146 $_[0]->{args
}->{base_url
} || ''
152 my $st = shift || '';
155 if (scalar(@_) % 2) {
156 $self->log('Bad url: ' . join(';', $st, @_));
159 my $ret = $self->base_url();
161 # strip all undefined or empty arguments
162 %args = map { $_, $args{$_} } grep { $args{$_} } keys(%args);
164 if ($self->{args
}->{static_urls
}) {
165 my $kn = scalar(keys(%args));
171 'RSS_COMMENTS' => 'rss-comments.xml',
172 'SITEMAP' => 'sitemap.xml',
173 'CSS' => 'style.css',
178 if (exists($p{$st})) {
179 return $ret . $p{$st};
184 if ($st eq 'INDEX' && $args{offset
}) {
185 return $ret . $args{offset
} . '.html';
187 if ($st eq 'TOPIC' && $args{topic
}) {
188 return $ret . $args{topic
} . '/';
190 if ($st eq 'SEARCH_BY_TAG' && $args{tag
}) {
191 return $ret . 'tag/' . $args{tag
} . '.html';
196 if ($st eq 'STORY' && $args{topic
} && $args{id
}) {
197 return $ret . $args{topic
} . '/' . $args{id
} . '.html';
199 if ($st eq 'TOPIC' && $args{topic
} && $args{offset
}) {
200 return $ret . $args{topic
} . '/~' . $args{offset
} . '.html';
202 if ($st eq 'SEARCH_BY_DATE' && $args{from
} && $args{to
}) {
203 # special case: full day search
204 $args{from
} =~ /^([0-9]{8})/;
207 if ($d && $args{from
} =~ /^${d}000000$/ && $args{to
} =~ /^${d}235959$/) {
208 return $ret . $d . '/';
211 return $ret . $args{from
} . '-' . $args{to
} . '.html';
215 # if static_urls is 2, *never* return non-static
216 if ($self->{args
}->{static_urls
} == 2) {
224 $ret .= '?' . join(';', map { "$_=$args{$_}" } sort keys(%args));
231 sub _topic_special_uri
{
233 my $topic_id = shift;
237 if (my $t = $self->source->topic($topic_id)) {
238 $ret = sprintf('<a href="%s">%s</a>',
239 $self->url('TOPIC', 'topic' => $topic_id),
244 $ret = "Bad topic $topic_id";
251 sub _story_special_uri
{
253 my $topic_id = shift;
254 my $story_id = shift;
258 if (my $s = $self->source->story($topic_id, $story_id)) {
260 if ($s->is_visible($self->auth())) {
261 $ret = sprintf('<a href = "%s">%s</a>',
263 'topic' => $topic_id,
270 $ret = $s->get('title');
274 my $topic = $self->source->topic($topic_id);
277 $ret = "Bad topic '$topic_id'";
280 $ret = "Bad story '$topic_id/$story_id'";
282 if ($topic->is_editor($self->auth())) {
283 $ret .= sprintf(' <a href = "%s">[+]</a>',
284 $self->url('EDIT_STORY',
285 'topic' => $topic_id,
297 sub _img_special_uri
{
304 # try to load Image::Size
305 eval("use Image::Size;");
308 # if available and image is found, add dimensions to <img>
309 my ($w, $h) = imgsize
('img/' . $src);
312 $more = sprintf('width = "%d" height = "%d"', $w, $h);
316 my $r = sprintf('<img src = "%simg/%s" %s />',
317 $self->base_url(), $src, $more
321 $r = "<span class = '$class'>" . $r . '</span>';
328 sub _thumb_special_uri
{
336 '<a href = "%simg/%s"><img src = "%simg/%s" width = "%d" /></a>',
341 ($self->{args
}->{thumbnail_size
} || 400)
345 $r = "<span class = '$class'>" . $r . '</span>';
352 sub _content_special_uri
{
354 my $topic_id = shift;
355 my $story_id = shift;
360 if (my $s = $self->source->story($topic_id, $story_id)) {
361 $ret = $self->special_uris($s->get($field));
364 $ret = "Bad story '$topic_id/$story_id'";
376 $string =~ s!topic://([\w\d_-]+)!$self->_topic_special_uri($1)!ge;
377 $string =~ s!story://([\w\d_-]+)/([\w\d_-]+)!$self->_story_special_uri($1,$2)!ge;
378 $string =~ s!img://([\w\d_\.-]+)/?([\w\d_-]*)!$self->_img_special_uri($1,$2)!ge;
379 $string =~ s!thumb://([\w\d_\.-]+)/?([\w\d_-]*)!$self->_thumb_special_uri($1,$2)!ge;
380 $string =~ s!body://([\w\d_-]+)/([\w\d_-]+)!$self->_content_special_uri($1,$2,'body')!ge;
381 $string =~ s!abstract://([\w\d_-]+)/([\w\d_-]+)!$self->_content_special_uri($1,$2,'abstract')!ge;
387 sub transfer_to_source
{
391 foreach my $id ($self->source->users()) {
392 my $u = $self->source->user($id);
393 $dst->insert_user($u);
396 foreach my $topic_id (sort $self->source->topics()) {
397 my $t = $self->source->topic($topic_id);
402 if ($nti =~ /-arch$/) {
403 # don't insert topic, just rename
407 $dst->insert_topic($t);
410 foreach my $id ($self->source->stories($topic_id)) {
412 # get story and its tags
413 my $s = $self->source->story($topic_id, $id);
414 my @tags = $s->tags();
417 $s->set('topic_id', $nti);
419 my $ns = $dst->insert_story($s);
427 foreach my $id ($self->source->templates()) {
428 my $t = $self->source->template($id);
429 $dst->insert_template($t);
439 my $g = bless( { @_ } , $class);
441 $g->{id
} ||= 'Gruta';
444 $g->{renderers_h
} = {};
447 if (ref($g->{sources
}) ne 'ARRAY') {
448 $g->{sources
} = [ $g->{sources
} ];
452 $g->{source
} = (@
{$g->{sources
}})[0];
457 $g->source->data($g);
460 if ($g->{renderers
}) {
461 if (ref($g->{renderers
}) ne 'ARRAY') {
462 $g->{renderers
} = [ $g->{renderers
} ];
465 foreach my $r (@
{$g->{renderers
}}) {
466 $g->{renderers_h
}->{$r->{renderer_id
}} = $r;
470 if ($g->{template
}) {
471 $g->template->data($g);
478 # ensure base_url has a final /
479 if ($g->{args
}->{base_url
} && $g->{args
}->{base_url
} !~ /\/$/) {
480 $g->{args
}->{base_url
} .= '/';