Version 2.0.0-rc1 RELEASED.
[gruta.git] / Gruta.pm
blobb834736f6cfd64fb706ef345e24c88a8030c3606
1 package Gruta;
3 use strict;
4 use warnings;
6 use Gruta::Data;
8 $Gruta::VERSION = '2.0.0-rc1';
10 sub sources { return @{$_[0]->{sources}}; }
11 sub template { return $_[0]->{template}; }
12 sub cgi { return $_[0]->{cgi}; }
14 sub log {
15 my $self = shift;
16 my $msg = shift;
18 print STDERR $self->{id}, ' ', scalar(localtime), ': ', $msg, "\n";
22 sub _call {
23 my $self = shift;
24 my $method = shift;
25 my $short = shift;
27 my @r = ();
29 foreach my $s ($self->sources()) {
30 if (my $m = $s->can($method)) {
31 my @pr = $m->($s, @_);
33 if (@pr && $pr[0]) {
34 @r = (@r, @pr);
36 last if $short;
41 return wantarray ? @r : $r[0];
44 sub topic { my $self = shift; return $self->_call('topic', 1, @_); }
45 sub topics { my $self = shift; return $self->_call('topics', 0); }
47 sub user { my $self = shift; return $self->_call('user', 1, @_); }
48 sub users { my $self = shift; return $self->_call('users', 0); }
50 sub story {
51 my $self = shift;
52 my $topic_id = shift;
53 my $id = shift;
55 if (! $topic_id || ! $id) {
56 return undef;
59 my $story = undef;
60 my $ck = $topic_id . '/' . $id;
62 if ($story = $self->{story_cache}->{$ck}) {
63 return $story;
66 if (not $story = $self->_call('story', 1, $topic_id, $id)) {
67 return undef;
70 my $format = $story->get('format') || 'grutatxt';
72 if (my $rndr = $self->{renderers_h}->{$format}) {
73 $rndr->story($story);
76 return $self->{story_cache}->{$ck} = $story;
80 sub stories { my $self = shift; return $self->_call('stories', 0, @_); }
81 sub stories_by_date { my $self = shift; return $self->_call('stories_by_date', 1, @_); }
83 sub search_stories {
84 my $self = shift;
85 my $topic_id = shift;
87 my @l = $self->_call('search_stories', 1, $topic_id, @_);
89 return sort { $self->story($topic_id, $a)->get('title') cmp
90 $self->story($topic_id, $b)->get('title') } @l;
93 sub stories_top_ten {
94 my $self = shift;
96 my @l = $self->_call('stories_top_ten', 0, @_);
98 return sort { $b->[0] cmp $a->[0] } @l;
101 sub search_stories_by_tag {
102 my $self = shift;
104 my @l = $self->_call('search_stories_by_tag', 0, @_);
106 return sort { $self->story($a->[0], $a->[1])->get('title') cmp
107 $self->story($b->[0], $b->[1])->get('title') } @l;
110 sub tags {
111 my $self = shift;
113 my @l = $self->_call('tags', 0, @_);
115 return sort { $a->[0] cmp $b->[0] } @l;
118 sub insert_topic { my $self = shift; return $self->_call('insert_topic', 1, @_); }
119 sub insert_user { my $self = shift; return $self->_call('insert_user', 1, @_); }
120 sub insert_story { my $self = shift; return $self->_call('insert_story', 1, @_); }
123 sub auth {
124 my $self = shift;
126 if (@_) { $self->{auth} = shift; } # Gruta::Data::User
128 return $self->{auth};
132 sub auth_from_sid {
133 my $self = shift;
134 my $sid = shift;
136 my $u = undef;
138 if ($sid) {
139 $self->_call('purge_old_sessions', 0);
141 if (my $session = $self->_call('session', 1, $sid)) {
142 $u = $session->source->user( $session->get('user_id') );
143 $u->set('sid', $sid);
144 $self->auth($u);
148 return $u;
152 sub login {
153 my $self = shift;
154 my $user_id = shift;
155 my $passwd = shift;
157 my $sid = undef;
159 if (my $u = $self->user( $user_id )) {
161 # account expired? go!
162 if (my $xdate = $u->get('xdate')) {
163 if (Gruta::Data::today() > $xdate) {
164 return undef;
168 my $p = $u->get('password');
170 if (crypt($passwd, $p) eq $p) {
171 # create new sid
172 $sid = time() . $$;
174 my $session = Gruta::Data::Session->new(
175 id => $sid,
176 time => time(),
177 user_id => $user_id
180 $u->source->insert_session( $session );
182 $u->set('sid', $sid);
183 $self->auth($u);
187 return $sid;
191 sub logout {
192 my $self = shift;
194 if (my $auth = $self->auth()) {
195 if( my $sid = $auth->get('sid')) {
196 if (my $session = $auth->source->session( $sid )) {
197 $session->delete() if $session->can('delete');
202 $self->auth( undef );
203 return $self;
207 sub _link_to_topic {
208 my $self = shift;
209 my $topic_id = shift;
211 my $ret = undef;
213 if (my $t = $self->topic($topic_id)) {
214 $ret = "<a href='?t=TOPIC;topic=$topic_id'>" .
215 $t->get('name') . '</a>';
217 else {
218 $ret = "Bad topic $topic_id";
221 return $ret;
225 sub _link_to_story {
226 my $self = shift;
227 my $topic_id = shift;
228 my $story_id = shift;
230 my $ret = undef;
232 if (my $s = $self->story($topic_id, $story_id)) {
233 $ret = "<a href='?t=STORY;topic=$topic_id;id=$story_id'>" .
234 $s->get('title') . '</a>';
236 else {
237 $ret = "Bad story '$topic_id/$story_id'";
240 return $ret;
244 sub special_uris {
245 my $self = shift;
246 my $string = shift;
248 $string =~ s!topic://([\w\d_]+)!$self->_link_to_topic($1)!ge;
249 $string =~ s!story://([\w\d_]+)/([\w\d_]+)!$self->_link_to_story($1,$2)!ge;
251 return $string;
255 sub transfer_to_source {
256 my $self = shift;
257 my $dst = shift;
259 foreach my $id ($self->users()) {
260 my $u = $self->user($id);
261 $dst->insert_user($u);
264 foreach my $topic_id (sort $self->topics()) {
265 my $t = $self->topic($topic_id);
267 my $nti = $topic_id;
269 # is it an archive?
270 if ($nti =~ /-arch$/) {
271 # don't insert topic, just rename
272 $nti =~ s/-arch$//;
274 else {
275 $dst->insert_topic($t);
278 foreach my $id ($self->stories($topic_id)) {
280 # get story and its tags
281 my $s = $self->story($topic_id, $id);
282 my @tags = $s->tags();
284 # set new topic
285 $s->set('topic_id', $nti);
287 my $ns = $dst->insert_story($s);
289 if (@tags) {
290 $ns->tags(@tags);
295 return $self;
299 sub flush_story_cache {
300 my $self = shift;
302 $self->{story_cache} = {};
306 sub new {
307 my $class = shift;
309 my $g = bless( { @_ } , $class);
311 $g->{id} ||= 'Gruta';
312 $g->{story_cache} = {};
313 $g->{renderers_h} = {};
315 if (ref($g->{sources}) ne 'ARRAY') {
316 $g->{sources} = [ $g->{sources} ];
319 if ($g->{renderers}) {
320 if (ref($g->{renderers}) ne 'ARRAY') {
321 $g->{renderers} = [ $g->{renderers} ];
324 foreach my $r (@{$g->{renderers}}) {
325 $g->{renderers_h}->{$r->{renderer_id}} = $r;
329 $g->template->data($g) if $g->{template};
330 $g->cgi->data($g) if $g->{cgi};
332 return $g;
335 sub run {
336 my $self = shift;
338 if ($self->{cgi}) {
339 $self->cgi->run();