Changed (again) Story's new_id().
[gruta.git] / Gruta / Data.pm
blob99e51cb64347785057d1bf2ea6fa1fc0807632ba
1 package Gruta::Data;
3 use strict;
4 use warnings;
6 package Gruta::Data::BASE;
8 use Carp;
10 sub fields {
11 return ();
14 sub vfields {
15 return ();
18 sub afields {
19 return ($_[0]->fields(), $_[0]->vfields());
22 sub filter_field {
23 return $_[2];
26 sub source {
27 my $self = shift;
29 if (@_) {
30 $self->{_source} = shift;
33 return $self->{_source};
37 sub _assert {
38 my $self = shift;
40 my $id = $self->get('id') || '';
41 $id =~ /^[\d\w_-]+$/ or confess "Bad id '$id' [" . ref($self) . '] ';
43 return $self;
46 sub new {
47 my $class = shift;
48 my %args = @_;
50 my $self = bless({ }, $class);
52 foreach my $k ($self->afields()) {
53 $self->{$k} = undef;
54 $self->set($k, $args{$k});
57 return $self;
60 sub get {
61 my $self = shift;
62 my $field = shift;
64 confess 'get ' . ref($self) . " field '$field'?" unless exists $self->{$field};
66 return $self->{$field};
69 sub set {
70 my $self = shift;
71 my $field = shift;
72 my $value = shift;
74 confess 'set ' . ref($self) . " field '$field'?" unless exists $self->{$field};
76 $self->{$field} = $self->filter_field($field, $value);
78 return $self->{$field};
82 package Gruta::Data::Topic;
84 use base 'Gruta::Data::BASE';
86 sub fields {
87 return qw(id name editors max_stories internal description);
90 sub filter_field {
91 my $self = shift;
92 my $field = shift;
93 my $value = shift;
95 # ensure empty numeric values are 0
96 if ($field =~ /^(max_stories|internal)$/ && !$value) {
97 $value = 0;
100 return $value;
103 sub is_editor {
104 my $self = shift;
105 my $user = shift; # Gruta::Data::User
107 return $user && ($user->get('is_admin') ||
108 ($self->get('editors') || '')
109 =~ m{\b$user->get('id')\b}) ? 1 : 0;
112 package Gruta::Data::Story;
114 use base 'Gruta::Data::BASE';
116 use Carp;
118 sub fields {
119 return qw(id topic_id title date date2 userid format hits ctime toc has_comments full_story content description abstract body);
122 sub filter_field {
123 my $self = shift;
124 my $field = shift;
125 my $value = shift;
127 # ensure empty numeric values are 0
128 if ($field =~ /^(hits|ctime)$/ && !$value) {
129 $value = 0;
132 return $value;
135 sub _assert {
136 my $self = shift;
138 $self->SUPER::_assert();
140 my $topic_id = $self->get('topic_id') || '';
141 $topic_id =~ /^[\d\w_-]+$/ or croak "Bad topic_id";
143 return $self;
146 sub date {
147 return Gruta::Data::format_date($_[0]->get('date'), $_[1]);
150 sub date2 {
151 return Gruta::Data::format_date($_[0]->get('date2'), $_[1]);
154 sub touch {
155 return $_[0];
158 sub tags {
159 my $self = shift;
160 my @ret = undef;
162 if (scalar(@_)) {
163 $self->set('tags', [ @_ ]);
165 else {
166 @ret = @{ $self->get('tags') };
169 return @ret;
172 sub new_id {
173 my $self = shift;
175 my $id;
177 my ($s, $m, $h, $d, $M, $y) = localtime(time());
179 $id = sprintf("%02x%c%c%c%c%02x",
180 $$ % 256,
181 (($y + $m) % 25) + 97,
182 (($d + $M) % 25) + 97,
183 ($h % 25) + 97,
184 ($s % 25) + 97,
185 rand(0xff)
188 return $id;
191 sub is_visible {
192 my $self = shift;
193 my $user = shift; # Gruta::Data::User
195 return !$user && $self->get('date') gt Gruta::Data::today() ? 0 : 1;
199 package Gruta::Data::User;
201 use base 'Gruta::Data::BASE';
203 sub fields {
204 return qw(id username email password can_upload is_admin xdate);
207 sub filter_field {
208 my $self = shift;
209 my $field = shift;
210 my $value = shift;
212 # ensure empty numeric values are 0
213 if ($field =~ /^(can_upload|is_admin)$/ && !$value) {
214 $value = 0;
217 return $value;
220 sub xdate {
221 return Gruta::Data::format_date($_[0]->get('xdate'), $_[1]);
224 sub password {
225 my $self = shift;
226 my $passwd = shift;
228 $self->set('password', Gruta::Data::crypt($passwd));
230 return $self;
234 package Gruta::Data::Session;
236 use base 'Gruta::Data::BASE';
238 sub fields {
239 return qw(id time user_id ip);
242 sub new {
243 my $class = shift;
245 my $sid = time() . $$;
247 return $class->SUPER::new( id => $sid, time => time(), @_);
251 package Gruta::Data::Template;
253 use base 'Gruta::Data::BASE';
255 sub fields {
256 return qw(id content);
260 package Gruta::Data::Comment;
262 use base 'Gruta::Data::BASE';
264 use Carp;
266 sub fields {
267 return qw(id topic_id story_id ctime date approved author email content);
271 sub filter_field {
272 my $self = shift;
273 my $field = shift;
274 my $value = shift;
276 if ($field eq 'approved' && !$value) {
277 $value = 0;
280 return $value;
284 sub date {
285 return Gruta::Data::format_date($_[0]->get('date'), $_[1]);
288 sub new {
289 my $class = shift;
291 my $id = sprintf("%08x%04x", time(), $$);
293 return $class->SUPER::new(
294 id => $id,
295 ctime => time(),
296 date => Gruta::Data::today(),
302 package Gruta::Data;
304 sub format_date {
305 my $date = shift;
306 my $format = shift;
308 if (!$date) {
309 return '';
312 if ($format) {
313 use POSIX;
315 my ($y, $m, $d, $H, $M, $S) = ($date =~
316 /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/);
318 # convert %y to %Y for compatibility
319 $format =~ s/%y/%Y/g;
321 $format = POSIX::strftime($format, $S, $M, $H,
322 $d, $m - 1, $y - 1900);
324 else {
325 $format = $date;
328 return $format;
332 $Gruta::Data::_today = undef;
334 sub today {
335 my $format = shift;
337 my $date = $Gruta::Data::_today;
339 if (!$date) {
340 my ($S, $M, $H, $d, $m, $y) = (localtime)[0..5];
342 $date = sprintf('%04d%02d%02d%02d%02d%02d',
343 1900 + $y, $m + 1, $d, $H, $M, $S);
345 $Gruta::Data::_today = $date;
348 return Gruta::Data::format_date($date, $format);
352 sub crypt {
353 my $key = shift;
354 my $salt = shift;
356 # no salt? pick one at random
357 if (!$salt) {
358 $salt = sprintf('%02d', rand(100));
361 return crypt($key, $salt);