1###############################################################################
2# ExtendedProfiles.pm                                                         #
3# $Date: 12.02.14 $                                                           #
4###############################################################################
5# YaBB: Yet another Bulletin Board                                            #
6# Version:        YaBB 2.6.11                                                 #
7# Packaged:       December 2, 2014                                            #
8# Distributed by: http://www.yabbforum.com                                    #
9# =========================================================================== #
10# Copyright (c) 2000-2014 YaBB (www.yabbforum.com) - All Rights Reserved.     #
11# Software by:  The YaBB Development Team                                     #
12#               with assistance from the YaBB community.                      #
13###############################################################################
14# This file was part of the Extended Profiles Mod which has been created by   #
15# Michael Prager. Last modification by him: 15.11.07                          #
16# Added to the YaBB default code on 07. September 2008                        #
17###############################################################################
18our $VERSION = '2.6.11';
19
20$extendedprofilespmver = 'YaBB 2.6.11 $Revision: 1611 $';
21if ( $action eq 'detailedversion' ) { return 1; }
22
23LoadLanguage('ExtendedProfiles');
24
25$ext_spacer_hr        = q~<hr class="hr" />~;
26$ext_spacer_br        = q~<br />~;
27$ext_max_email_length = 60;
28$ext_max_url_length   = 100;
29$ext_max_image_length = 100;
30
31my %field;
32
33# outputs the value of a user's extended profile field
34## USAGE: $value = ext_get("admin","my_custom_fieldname");
35##  or    $value_raw = ext_get("admin","my_custom_fieldname",1);
36## pass the third argument if you want to get the raw content e.g. an unformatted date
37sub ext_get {
38    my (
39        $pusername, $fieldname, $no_parse,           @ext_profile,
40        @options,   $field,     $id,                 $value,
41        $width,     $height,    @allowed_extensions, $extension,
42        $match
43    ) = ( shift, shift, shift );
44    ext_get_profile($pusername);
45    $id    = ext_get_field_id($fieldname);
46    $value = ${ $uid . $pusername }{ 'ext_' . $id };
47    if ( $no_parse eq q{} || $no_parse == 0 ) {
48        $field = ext_get_field($id);
49        if ( $field{'type'} eq 'text' ) {
50            @options = split /\^/xsm, $field{'options'};
51            if ( $options[3] ne q{} && $value eq q{} ) { $value = $options[3]; }
52            if ( $options[4] == 1 ) {
53                $value = ext_parse_ubbc( $value, $pusername );
54            }
55
56        }
57        elsif ( $field{'type'} eq 'text_multi' && $value ne q{} ) {
58            @options = split /\^/xsm, $field{'options'};
59            if ( $options[3] == 1 ) {
60                $value = ext_parse_ubbc( $value, $pusername );
61            }
62
63        }
64        elsif ( $field{'type'} eq 'select' ) {
65            @options = split /\^/xsm, $field{'options'};
66            if ( $value > $#options || $value eq q{} ) { $value = 0; }
67            $value = $options[$value];
68
69        }
70        elsif ( $field{'type'} eq 'radiobuttons' ) {
71            @options = split /\^/xsm, $field{'options'};
72            if ( $value > $#options ) { $value = 0; }
73            if ( !$field{'radiounselect'} && $value eq q{} ) { $value = 0; }
74            if ( $value ne q{} ) { $value = $options[$value]; }
75
76        }
77        elsif ( $field{'type'} eq 'date' && $value ne q{} ) {
78            @mytime = split /\//xsm, $value;
79            $mytime = timelocal(0,0,0, $mytime[1],$mytime[0]-1,$mytime[2]);
80            $mytime =  timeformatcal($mytime);
81            $value = dtonly ($mytime);
82        }
83        elsif ( $field{'type'} eq 'checkbox' ) {
84            if   ( $value == 1 ) { $value = $lang_ext{'true'} }
85            else                 { $value = $lang_ext{'false'} }
86
87        }
88        elsif ( $field{'type'} eq 'spacer' ) {
89            @options = split /\^/xsm, $field{'options'};
90            if   ( $options[0] == 1 ) { $value = qq~$ext_spacer_br~; }
91            else                      { $value = qq~$ext_spacer_hr~; }
92
93        }
94        elsif ( $field{'type'} eq 'url' && $value ne q{} ) {
95            if ( $value !~ m{\Ahttp://}sm ) { $value = "http://$value"; }
96
97        }
98        elsif ( $field{'type'} eq 'image' && $value ne q{} ) {
99            @options = split /\^/xsm, $field{'options'};
100            if ( $options[2] ne q{} ) {
101                @allowed_extensions = split /\ /xsm, $options[2];
102                $match = 0;
103                foreach my $extension (@allowed_extensions) {
104                    if ( grep { /$extension$/ism } $value ) {
105                        $match = 1;
106                        last;
107                    }
108                }
109                if ( $match == 0 ) { return q{}; }
110            }
111            if ( $options[0] ne q{} && $options[0] != 0 ) {
112                $width = q~ width="~ . ( $options[0] + 0 ) . q~"~;
113            }
114            else { $width = q{}; }
115            if ( $options[1] ne q{} && $options[1] != 0 ) {
116                $height = q~ height="~ . ( $options[1] + 0 ) . q~"~;
117            }
118            else { $height = q{}; }
119            if ( $value !~ m{\Ahttp://}sm ) { $value = "http://$value"; }
120            $value = qq~<img src="$value" class="vtop"$width$height alt=q{} />~;
121        }
122    }
123
124    return $value;
125}
126
127# loads the (extended) profile of a user
128sub ext_get_profile {
129    LoadUser(shift);
130    return;
131}
132
133# returns an array of the form qw(ext_0 ext_1 ext_2 ...)
134sub ext_get_fields_array {
135    my ( $count, @result ) = (0);
136    foreach (@ext_prof_fields) {
137        push @result, "ext_$count";
138        $count++;
139    }
140    return @result;
141}
142
143# returns the id of a field through the fieldname
144sub ext_get_field_id {
145    my ( $fieldname, $count, $id, $current, $currentname, $dummy ) =
146      ( shift, 0 );
147    foreach my $current (@ext_prof_fields) {
148        ( $currentname, $dummy ) = split /\|/xsm, $current;
149        if ( $currentname eq $fieldname ) { $id = $count; last; }
150        $count++;
151    }
152    return $id;
153}
154
155# returns all settings of a specific field
156sub ext_get_field {
157    $field{'id'} = shift;
158
159    (
160        $field{'name'},                   $field{'type'},
161        $field{'options'},                $field{'active'},
162        $field{'comment'},                $field{'required_on_reg'},
163        $field{'visible_in_viewprofile'}, $field{'v_users'},
164        $field{'v_groups'},               $field{'visible_in_posts'},
165        $field{'p_users'},                $field{'p_groups'},
166        $field{'p_displayfieldname'},     $field{'visible_in_memberlist'},
167        $field{'m_users'},                $field{'m_groups'},
168        $field{'editable_by_user'},       $field{'visible_in_posts_popup'},
169        $field{'pp_users'},               $field{'pp_groups'},
170        $field{'pp_displayfieldname'},    $field{'radiounselect'},
171        undef
172    ) = split /\|/xsm, $ext_prof_fields[ $field{'id'} ];
173    return;
174}
175
176# returns whenever the current user is allowed to view a field or not
177sub ext_has_access {
178    my (
179        $allowed_users, $allowed_groups, $access,  $usergroup,
180        $useraddgroup,  $postcount,      $user,    @users,
181        $group,         @groups,         $groupid, $postamount
182      )
183      = (
184        shift, shift, 0,
185        ${ $uid . $username }{'position'},
186        ${ $uid . $username }{'addgroups'},
187        ${ $uid . $username }{'postcount'}, undef,
188      );
189
190    if ( ( $allowed_users ne q{} ) || ( $allowed_groups ne q{} ) ) {
191        if ( $allowed_users ne q{} ) {
192            @users = split /\,/xsm, $allowed_users;
193            foreach my $user (@users) {
194                if ( $user eq $username ) { $access = 1; return $access; }
195            }
196        }
197        if ( $allowed_groups ne q{} ) {
198
199# generate list of allowed groups
200# example: @groups = ('Administrator', 'Moderator', 'Global Moderator', 'Post{-1}', 'NoPost{1}');
201            @groups = split /\s*\,\s*/xsm, $allowed_groups;
202            for my $group (@groups) {
203
204                # check if user is in one of these groups
205            if (   $group eq 'Administrator'
206                || $group eq 'Moderator'
207                || $group eq 'Mid Moderator'
208                || $group eq 'Global Moderator' )
209                {
210                    if ( $group eq $usergroup ) { $access = 1; return $access; }
211                }
212                elsif ( $group =~ m/^NoPost{(\d+)}$/sm ) {
213
214                    # check if user is on a post-independent group
215                    $groupid = $1;
216
217                    # check if group exists at all
218                    if ( exists $NoPost{$groupid} && $groupid ne q{} ) {
219
220                       # check if group id is in user position or addgroup field
221                        if ( $usergroup eq $groupid ) {
222                            $access = 1;
223                            return $access;
224                        }
225                        foreach my $group ( split /,/xsm, $useraddgroup ) {
226                            if ( $group eq $groupid ) {
227                                $access = 1;
228                                return $access;
229                            }
230                        }
231                    }
232                }
233                elsif ( $group =~ m/^Post{(\d+)}$/sm ) {
234
235                    # check if user is in one of the post-depending groups...
236                    $groupid = $1;
237                    foreach my $postamount ( reverse sort { $a <=> $b } keys %Post ) {
238                        if ( $postcount > $postamount ) {
239
240                            # found the group the user is in
241                            if ( $postamount eq $groupid ) {
242                                $access = 1;
243                                return $access;
244                            }
245                        }
246                    }
247                }
248            }
249        }
250    }
251    else { $access = 1; }
252
253    return $access;
254}
255
256# applies UBBC code to a string
257sub ext_parse_ubbc {
258    my ( $source, $displayname ) = @_;
259    my $temp = $message;
260    $message = $source;
261    require Sources::YaBBC;
262    $displayname = $pusername;    # must be set for /me tag
263    DoUBBC();
264    ToChars($message);
265    $source  = $message;
266    $message = $temp;
267    return $source;
268}
269
270# returns the output for the viewprofile page
271sub ext_viewprofile {
272    my (
273        $pusername, @ext_profile, $field,         $id,
274        $output,    $fieldname,   @options,       $value,
275        $previous,  $count,       $last_field_id, $pre_output
276    ) = (shift);
277
278    if ( $#ext_prof_order > 0 ) {
279        $last_field_id = ext_get_field_id( $ext_prof_order[-1] );
280    }
281
282    foreach my $fieldname (@ext_prof_order) {
283        $id = ext_get_field_id($fieldname);
284        ext_get_field($id);
285        $value = ext_get( $pusername, $fieldname );
286
287 # make sure the field is visible and the user allowed to view the current field
288        if (   $field{'visible_in_viewprofile'} == 1
289            && $field{'active'} == 1
290            && ext_has_access( $field{'v_users'}, $field{'v_groups'} ) )
291        {
292            if ( $output eq q{} && $previous != 1 ) {
293                $pre_output = $ext_pre_output;
294                $previous   = 1;
295            }
296
297            # format the output dependend of the field type
298            if (   ( $field{'type'} eq 'text' && $value ne q{} )
299                || ( $field{'type'} eq 'text_multi'   && $value ne q{} )
300                || ( $field{'type'} eq 'select'       && $value ne q{ } )
301                || ( $field{'type'} eq 'radiobuttons' && $value ne q{} )
302                || ( $field{'type'} eq 'date'         && $value ne q{} )
303                || $field{'type'} eq 'checkbox' )
304            {
305                $output .= qq~
306            <div class="ext_lft">
307            <b>$field{'name'}:</b>
308            </div>
309            <div class="ext_rgt">
310            $value&nbsp;
311            </div>~;
312                $previous = 0;
313
314            }
315            elsif ( $field{'type'} eq 'spacer' ) {
316
317# only print spacer if the previous entry was no spacer of the same type and if this is not the last entry
318                if ( ( $previous == 0 || $field{'comment'} ne q{} )
319                    && $id ne $last_field_id )
320                {
321                    if ( $value eq $ext_spacer_br ) {
322                        $output .= qq~
323            <div class="ext_100">
324            $ext_spacer_br
325            </div>~;
326                        $previous = 0;
327                    }
328                    else {
329                        $output .= $ext_output_a;
330                        if ( $field{'comment'} ne q{} ) {
331                            $output .= $ext_output_c;
332                        }
333                        else {
334                            $output .= $ext_output_b;
335                        }
336                        $previous = 1;
337                    }
338                }
339
340            }
341            elsif ( $field{'type'} eq 'email' && $value ne q{} ) {
342                $output .= qq~
343            <div class="ext_lft">
344            <b>$field{'name'}:</b>
345            </div>
346            <div class="ext_rgt">
347            ~ . enc_eMail( $img_txt{'69'}, $value, q{}, q{} ) . q~
348            </div>~;
349                $previous = 0;
350
351            }
352            elsif ( $field{'type'} eq 'url' && $value ne q{} ) {
353                $output .= qq~
354            <div class="ext_lft">
355            <b>$field{'name'}:</b>
356            </div>
357            <div class="ext_rgt">
358            <a href="$value" target="_blank">$value</a>
359            </div>~;
360                $previous = 0;
361
362            }
363            elsif ( $field{'type'} eq 'image' && $value ne q{} ) {
364                $output .= qq~
365            <div class="ext_lft">
366            <b>$field{'name'}:</b>
367            </div>
368            <div class="ext_rgt">
369            $value
370            </div>~;
371                $previous = 0;
372            }
373        }
374    }
375
376    # only add spacer if there there is at least one field displayed
377    if ( $output ne q{} ) {
378        $output = $pre_output . $output . q~
379        </td>
380    </tr>~;
381    }
382    return $output;
383}
384
385# returns the output for the post page
386sub ext_viewinposts {
387    my (
388        $pusername, $popup,    @ext_profile, $field,
389        $id,        $output,   $fieldname,   @options,
390        $value,     $previous, $pre_output,  $visible,
391        $users,     $groups,   $displayfieldname
392    ) = ( shift, shift );
393
394    if ( $pusername ne 'Guest' ) {
395        foreach my $fieldname (@ext_prof_order) {
396            $id    = ext_get_field_id($fieldname);
397            $field = ext_get_field($id);
398            $value = ext_get( $pusername, $fieldname );
399
400            if ( $popup ne q{} ) {
401                $visible          = $field{'visible_in_posts_popup'};
402                $users            = $field{'pp_users'};
403                $groups           = $field{'pp_groups'};
404                $displayfieldname = $field{'pp_displayfieldname'};
405            }
406            else {
407                $visible          = $field{'visible_in_posts'};
408                $users            = $field{'p_users'};
409                $groups           = $field{'p_groups'};
410                $displayfieldname = $field{'p_displayfieldname'};
411            }
412
413 # make sure the field is visible and the user allowed to view the current field
414            if (   $visible == 1
415                && $field{'active'} == 1
416                && ext_has_access( $users, $groups ) )
417            {
418                if ( $displayfieldname == 1 ) {
419                    $displayedfieldname = "$field{'name'}: ";
420                }
421                else { $displayedfieldname = q{}; }
422                if ( $output eq q{} ) { $output = qq~$ext_spacer_br\n~; }
423
424                # format the output depending on the field type
425                if (   ( $field{'type'} eq 'text' && $value ne q{} )
426                    || ( $field{'type'} eq 'text_multi'   && $value ne q{} )
427                    || ( $field{'type'} eq 'select'       && $value ne q{ } )
428                    || ( $field{'type'} eq 'radiobuttons' && $value ne q{} )
429                    || ( $field{'type'} eq 'date'         && $value ne q{} )
430                    || $field{'type'} eq 'checkbox' )
431                {
432                    $output .= qq~$displayedfieldname$value<br />\n~;
433                    $previous = q{};
434                }
435                elsif ( $field{'type'} eq 'spacer' ) {
436
437                    # those tags are required to keep the doc XHTML 1.0 valid
438                    if ( $previous ne "</small>$value<small>" ) {
439                        $previous = qq~</small>$value<small>~;
440                        $output .= $previous;
441                    }
442                }
443                elsif ( $field{'type'} eq 'email' && $value ne q{} ) {
444                    $output .=
445                        $displayedfieldname
446                      . enc_eMail( $img_txt{'69'}, $value, q{}, q{} )
447                      . qq~<br />\n~;
448                    $previous = q{};
449                }
450                elsif ( $field{'type'} eq 'url' && $value ne q{} ) {
451                    $output .=
452qq~$displayedfieldname<a href="$value" target="_blank">$value</a><br />\n~;
453                    $previous = q{};
454                }
455                elsif ( $field{'type'} eq 'image' && $value ne q{} ) {
456                    $output .= qq~$displayedfieldname$value<br />\n~;
457                    $previous = q{};
458                }
459            }
460        }
461    }
462
463# check if there we have any output (except spacers) at all. If so, return empty output
464    $pre_output = $output;
465    $pre_output =~
466s/(?:\<\/small>(?:(?:$ext_spacer_hr)|(?:$ext_spacer_br))<small>)|\n|(?:\<br(?: \/)?>)//igsm;
467    if ( $pre_output eq q{} ) { $output = q{}; }
468
469    return $output;
470}
471
472{
473
474    # we need a "static" variable to produce unique element ids
475    my $ext_usercount = 0;
476
477    # returns the output for the post page (popup box)
478    sub ext_viewinposts_popup {
479        my ( $pusername, $link, $output ) = ( shift, shift );
480        $output = ext_viewinposts( $pusername, 'popup' );
481        $output =~ s/^$ext_spacer_br\n//igxsm;
482        if ( $output ne q{} ) {
483            $link =~
484s/<a /<a onmouseover="document.getElementById('ext_$ext_usercount').style.visibility = 'visible'" onmouseout="document.getElementById('ext_$ext_usercount').style.visibility = 'hidden'" /igsm;
485            $output =
486qq~$link<div id="ext_$ext_usercount" class="code" style="visibility:hidden; position:absolute; z-index:1; width:auto;">$output</div>~;
487            $ext_usercount++;
488        }
489        else {
490            $output = $link;
491        }
492
493        return $output;
494    }
495}
496
497# returns the output for the table header in memberlist
498sub ext_memberlist_tableheader {
499    my ( $output, $fieldname );
500
501    foreach my $fieldname (@ext_prof_order) {
502        $field = ext_get_field( ext_get_field_id($fieldname) );
503
504 # make sure the field is visible and the user allowed to view the current field
505        if (   $field{'visible_in_memberlist'} == 1
506            && $field{'active'} == 1
507            && ext_has_access( $field{'m_users'}, $field{'m_groups'} ) )
508        {
509            $output .= $ext_memberlist_tableheader;
510            $output =~ s/{yabb ext_fieldname}/$field{'name'}/sm;
511        }
512    }
513
514    return $output;
515}
516
517# returns the number of additional fields showed in memberlist
518sub ext_memberlist_get_headercount {
519
520# count the linebreaks to get the number of additional <td>s for the memberlist table
521    my ( $headers, $headercount ) = ( shift, 0 );
522    $headers =~ s/(\n)/ $headercount++ /egm;
523    return $headercount;
524}
525
526# returns the output for the table tds in memberlist
527sub ext_memberlist_tds {
528    my (
529        $pusername, $usergroup, @ext_profile, $field,
530        $id,        $output,    $access,      @users,
531        $user,      @groups,    $group,       $fieldname,
532        @options,   $count,     $color,       $value
533    ) = ( shift, ${ $uid . $username }{'position'} );
534
535    $count = 0;
536    foreach my $fieldname (@ext_prof_order) {
537        $id    = ext_get_field_id($fieldname);
538        $field = ext_get_field($id);
539        $value = ext_get( $pusername, $fieldname );
540
541 # make sure the field is visible and the user allowed to view the current field
542        if (   $field{'visible_in_memberlist'} == 1
543            && $field{'active'} == 1
544            && ext_has_access( $field{'m_users'}, $field{'m_groups'} ) == 1 )
545        {
546            $color = $count % 2 == 1 ? 'windowbg' : 'windowbg2';
547
548            $td_attributs = qq~class="$color"~;
549
550            #}
551            if ( $field{'type'} eq 'email' ) {
552                if ( $value ne q{} ) {
553                    $value = enc_eMail( $img_txt{'69'}, $value, q{}, q{} );
554                }
555            }
556            elsif ( $field{'type'} eq 'url' ) {
557                if ( $value ne q{} ) {
558                    $value = qq~<a href="$value" target="_blank">$value</a>~;
559                }
560            }
561            if ( $value eq q{} ) { $value .= '&nbsp;'; }
562            $output .= $ext_memberlist_td;
563            $output =~ s/{yabb ext_td_attributs}/$td_attributs/sm;
564            $output =~ s/{yabb ext_value}/$value/sm;
565            $count++;
566        }
567    }
568    return $output;
569}
570
571# returns the edit mask of a field (used on registration and edit profile page)
572sub ext_gen_editfield {
573    my (
574        $id,              $pusername,  @ext_profile, $output,
575        $field,           @options,    $selected,    $count,
576        $required_prefix, $dayormonth, $dayormonthd, $dayormonthm,
577        $value,           $template1,  $template2
578    ) = ( shift, shift );
579
580    LoadLanguage('Profile');
581    if ( $action eq 'register' ) {
582        get_template('Register');
583    }
584    else {
585        get_template('MyProfile');
586    }
587
588    $field = ext_get_field($id);
589
590    # if username is omitted, we'll generate the code for the registration page
591    if ( $pusername ne q{} ) {
592        $value = ext_get( $pusername, $field{'name'}, 1 );
593    }
594
595    FromHTML( $field{'comment'} );
596
597    $template1 = $ext_template1;
598    $template1 =~ s/{yabb fieldname}/$field{'name'}/sm;
599    $template1 =~ s/{yabb fieldcomment}/$field{'comment'}/sm;
600
601    if ( $field{'required_on_reg'} == 1 ) { $template2 = $myreg_req; }
602    $template2 .= $ext_endrow;
603
604    # format the output depending on field type
605    my $name_id = "ext_$id";
606    if ( $field{'type'} eq 'text' ) {
607        @options = split /\^/xsm, $field{'options'};
608        if ( $options[0] ne q{} ) {
609            $options[0] = qq~ maxlength="$options[0]"~;
610        }
611        if ( $options[1] ne q{} ) { $options[1] = qq~ size="$options[1]"~; }
612        if ( $options[3] ne q{} && $value eq q{} ) {
613            $options[3] = qq~ value="$options[3]"~;
614        }
615        else { $options[3] = qq~ value="$value"~; }
616        $output .=
617            $template1
618          . qq~<input type="text"$options[0] name="ext_$id" id="ext_$id"$options[1] $options[3] />~
619          . $template2;
620
621    }
622    elsif ( $field{'type'} eq 'text_multi' ) {
623        @options = split /\^/xsm, $field{'options'};
624        if ( $options[0] ) {
625            $field{'options'} = qq~
626    <br /><span class="small">$lang_ext{'max_chars1'}$options[0]$lang_ext{'max_chars2'} <input value="$options[0]" size="~
627              . length( $options[0] )
628              . q~" name="ext_~
629              . $id
630              . qq~_msgCL" readonly="readonly" disabled="disabled"$ext_msgCL /></span>
631    <script type="text/javascript">
632    var ext_~ . $id . q~_supportsKeys = false;
633    function ext_~ . $id . q~_tick() {
634      ext_~ . $id . q~_calcCharLeft(document.forms[0])
635      if (!ext_~
636              . $id
637              . q~_supportsKeys) timerID = setTimeout("ext_~
638              . $id
639              . qq~_tick()",$options[0])
640    }
641
642    function ext_~ . $id . qq~_calcCharLeft(sig) {
643      clipped = false;
644      maxLength = $options[0];
645      if (document.creator.ext_~ . $id . q~.value.length > maxLength) {
646        document.creator.ext_~
647              . $id
648              . q~.value = document.creator.ext_~
649              . $id
650              . q~.value.substring(0,maxLength);
651        charleft = 0;
652        clipped = true;
653        } else {
654        charleft = maxLength - document.creator.ext_~ . $id . q~.value.length;
655        }
656      document.creator.ext_~ . $id . q~_msgCL.value = charleft;
657      return clipped;
658    }
659    ext_~ . $id . q~_tick();
660    </script>~;
661        }
662        else { $field{'options'} = q{}; }
663        if   ( $options[1] ne q{} ) { $options[1] = qq~ rows="$options[1]"~; }
664        else                        { $options[1] = q~ rows="4"~; }
665        if   ( $options[2] ne q{} ) { $options[2] = qq~ cols="$options[2]"~; }
666        else                        { $options[2] = q~ cols="50"~; }
667        $value =~ s/<br(?: ?\/)?>/\n/gm;
668        $output .=
669            $template1
670          . qq~<textarea name="ext_$id" id="ext_$id"$options[1]$options[2]>$value</textarea>$field{'options'}~
671          . $template2;
672
673    }
674    elsif ( $field{'type'} eq 'select' ) {
675        $output .=
676          $template1 . qq~<select name="ext_$id" id="ext_$id" size="1">\n~;
677        @options = split /\^/xsm, $field{'options'};
678        if ( $value > $#options || $value eq q{} ) { $ext_profile[$id] = 0; }
679        $count = 0;
680        foreach (@options) {
681            if   ( $count == $value ) { $selected = ' selected="selected"'; }
682            else                      { $selected = q{}; }
683            $output .= qq~<option value="$count"$selected>$_</option>\n~;
684            $count++;
685        }
686        $output .= q~</select>~ . $template2;
687
688    }
689    elsif ( $field{'type'} eq 'radiobuttons' ) {
690        $output .= $template1;
691        @options = split /\^/xsm, $field{'options'};
692        if ( $value > $#options ) { $value = 0; }
693        if ( !$field{'radiounselect'} && $value eq q{} ) { $value = 0; }
694        $count = 0;
695        foreach (@options) {
696            if ( $value ne q{} && $count == $value ) {
697                $selected = qq~ id="ext_$id" checked="checked"~;
698            }
699            else { $selected = q{}; }
700            $output .=
701qq~<input type="radio" name="ext_$id" value="$count"$selected />$_\n~;
702            $count++;
703        }
704        $output .= $template2;
705
706    }
707    elsif ( $field{'type'} eq 'date' ) {
708        if ( $value !~ /[0-9\/]/sm ) { $value = q{}; }
709        @options = split /\//xsm, $value;
710        $dayormonthm =
711qq~ $profile_txt{'564'} <input type="text" name="ext_$id\_month" id="ext_$id\_month" size="2" maxlength="2" value="$options[0]" />~;
712        $dayormonthd =
713qq~ $profile_txt{'565'} <input type="text" name="ext_$id\_day" id="ext_$id\_day" size="2" maxlength="2" value="$options[1]" />~;
714        if (
715            (
716                   ${ $uid . $pusername }{'timeselect'} == 2
717                || ${ $uid . $pusername }{'timeselect'} == 3
718                || ${ $uid . $pusername }{'timeselect'} == 6
719            )
720            || (   $timeselected == 2
721                || $timeselected == 3
722                || $timeselected == 6 )
723          )
724        {
725            $dayormonth = $dayormonthd . $dayormonthm;
726            $name_id    = "ext_$id\_day";
727        }
728        else {
729            $dayormonth = $dayormonthm . $dayormonthd;
730            $name_id    = "ext_$id\_month";
731        }
732        $output .=
733            $template1
734          . qq~<span class="small">$dayormonth $profile_txt{'566'} <input type="text" name="ext_$id\_year" size="4" maxlength="4" value="$options[2]" /></span>~
735          . $template2;
736
737    }
738    elsif ( $field{'type'} eq 'checkbox' ) {
739        if   ( $value == 1 ) { $value = ' checked="checked"'; }
740        else                 { $value = q{}; }
741
742# we have to use a little trick here to get a value from a checkbox if it has been unchecked by adding a hidden <input value=""> before it
743        $output .=
744            $template1
745          . qq~<input type="hidden" name="ext_$id" value="" /><input type="checkbox" name="ext_$id" id="ext_$id"$value />~
746          . $template2;
747
748    }
749    elsif ( $field{'type'} eq 'spacer' ) {
750        @options = split /\^/xsm, $field{'options'};
751        if ( $options[1] == 1 ) {
752
753            $output .= $ext_spacer;
754            $output =~ s/{yabb fieldcomment}/$field{'comment'}/sm;
755        }
756
757    }
758    elsif ( $field{'type'} eq 'email' ) {
759        $output .=
760            $template1
761          . qq~<input type="text" name="ext_$id" id="ext_$id" maxlength="$ext_max_email_length" size="30" value="$value" />~
762          . $template2;
763
764    }
765    elsif ( $field{'type'} eq 'url' ) {
766        $output .=
767            $template1
768          . qq~<input type="text" name="ext_$id" id="ext_$id" maxlength="$ext_max_url_length" size="50" value="$value" />~
769          . $template2;
770
771    }
772    elsif ( $field{'type'} eq 'image' ) {
773        if ( $value eq q{} ) { $value = 'http://'; }
774        $output .=
775            $template1
776          . qq~<input type="text" name="ext_$id" id="ext_$id" maxlength="$ext_max_image_length" size="50" value="$value" />~
777          . $template2;
778    }
779    $output =~ s/<label for="">/<label for="$name_id">/gsm;
780
781    return $output;
782}
783
784# returns the output for the edit profile page
785## USAGE: $value = ext_editprofile("admin","required");
786sub ext_editprofile {
787    my (
788        $pusername, $part,      $usergroup, $field,    $id,
789        $output,    $fieldname, @options,   $selected, $count
790    ) = ( shift, shift, ${ $uid . $username }{'position'} );
791
792    get_gmod();
793    foreach my $fieldname (@ext_prof_order) {
794        $id = ext_get_field_id($fieldname);
795        ext_get_field($id);
796
797# make sure the field is visible, the user allowed to edit the current field and only the requested fields are returned
798        if (
799            $field{'active'} == 1
800            && (   $field{'editable_by_user'} != 0
801                || $iamadmin
802                || $iamgmod && $allow_gmod_profile )
803            && (
804                ( $part eq 'required' && $field{'required_on_reg'} == 1 )
805                ||    # show all required fields
806                ( $part eq 'additional' && $field{'required_on_reg'} != 1 )
807                ||    # show all additional fields
808                ( $part eq 'admin' && $field{'editable_by_user'} == 0 )
809                ||    # all fields for "admin edits" page
810                ( $part eq 'edit' && $field{'editable_by_user'} == 1 )
811                ||    # all fields for "edit profile" page
812                ( $part eq 'contact' && $field{'editable_by_user'} == 2 )
813                ||    # contact information page
814                ( $part eq 'options' && $field{'editable_by_user'} == 3 )
815                ||    # options page
816                ( $part eq 'im' && $field{'editable_by_user'} == 4 )
817            )
818          )
819        {             # im prefs page
820            $output .= ext_gen_editfield( $id, $pusername );
821        }
822    }
823
824    return $output;
825}
826
827# returns the output for the registration page
828sub ext_register {
829    my ( $id, $output, $fieldname, @options );
830
831    foreach my $fieldname (@ext_prof_order) {
832        $id = ext_get_field_id($fieldname);
833        ext_get_field($id);
834        if ( $field{'active'} == 1 && $field{'required_on_reg'} != 0 ) {
835            $output .= ext_gen_editfield($id);
836        }
837    }
838
839    return $output;
840}
841
842# returns if the submitted profile is valid, if not, return error messages
843sub ext_validate_submition {
844    my (
845        $username,   $pusername, $usergroup, %newprofile,
846        @oldprofile, $output,    $key,       $value,
847        $id,         $field,     @options
848    ) = ( shift, shift, ${ $uid . $username }{'position'}, %FORM );
849
850    get_gmod();
851
852    while ( ( $key, $value ) = each %newprofile ) {
853
854        # only validate fields with prefix "ext_"
855        if ( $key =~ /^ext_(\d+)/xsm ) {
856            $id = $1;
857            ext_get_field($id);
858
859            if ( !$field{'name'} ) {
860                $output .=
861                    $lang_ext{'field_not_existing1'}
862                  . $id
863                  . $lang_ext{'field_not_existing2'}
864                  . "<br />\n";
865            }
866
867            # check if user is allowed to modify this setting
868            if ( $action eq 'register2' ) {
869
870# if we're on registration page, ignore the 'editable_by_user' setting in case that 'required_on_reg' is set
871                if (   $field{'editable_by_user'} == 0
872                    && $field{'required_on_reg'} == 0 )
873                {
874                    $output .=
875                        $field{'name'} . q{: }
876                      . $lang_ext{'not_allowed_to_modify'}
877                      . "<br />\n";
878                }
879            }
880            elsif (
881                ( $field{'editable_by_user'} == 0 || $username ne $pusername )
882                && !$iamadmin
883                && ( !$iamgmod || !$allow_gmod_profile ) )
884            {
885                $output .=
886                    $field{'name'} . q{: }
887                  . $lang_ext{'not_allowed_to_modify'}
888                  . "<br />\n";
889            }
890
891            # check if setting is valid
892            if ( $field{'type'} ne 'text_multi' && $value =~ /[\n\r]/xsm ) {
893                $output .=
894                    $field{'name'} . q{: }
895                  . $lang_ext{'invalid_char'}
896                  . "<br />\n";
897            }
898
899            if ( $field{'type'} eq 'text' ) {
900                @options = split /\^/xsm, $field{'options'};
901
902# don't fill it with default value yet, it might be required on registration
903# if ($options[3] ne q{} && $value eq "") { $value = $options[3]; $newprofile{'ext_'.$id} = $value; }
904                if ( $options[0] + 0 > 0 && length($value) > $options[0] ) {
905                    $output .=
906                        $field{'name'} . q{: }
907                      . $lang_ext{'too_long'}
908                      . "<br />\n";
909                }
910                if (   $options[2] == 1
911                    && $value !~ /[0-9\.,]+/xsm
912                    && $value ne q{} )
913                {
914                    $output .=
915                        $field{'name'} . q{: }
916                      . $lang_ext{'not_numeric'}
917                      . "<br />\n";
918                }
919                FromChars($value);
920                ToHTML($value);
921                ToChars($value);
922
923            }
924            elsif ( $field{'type'} eq 'text_multi' ) {
925                @options = split /\^/xsm, $field{'options'};
926                if ( $options[0] + 0 > 0 && length($value) > $options[0] ) {
927                    $output .=
928                        $field{'name'} . q{: }
929                      . $lang_ext{'too_long'}
930                      . "<br />\n";
931                }
932                FromChars($value);
933                ToHTML($value);
934                ToChars($value);
935                $value =~ s/\n/<br \/>/gxsm;
936                $value =~ s/\r//gxsm;
937
938            }
939            elsif ($field{'type'} eq 'select'
940                || $field{'type'} eq 'radiobuttons' )
941            {
942                @options = split /\^/xsm, $field{'options'};
943                if ( $value !~ /[0-9]/xsm ) {
944                    $output .=
945                        $field{'name'} . q{: }
946                      . $lang_ext{'not_numeric'}
947                      . "<br />\n";
948                }
949                if ( $value < 0 ) {
950                    $output .=
951                        $field{'name'} . q{: }
952                      . $lang_ext{'too_small'}
953                      . "<br />\n";
954                }
955                if ( $value > $#options ) {
956                    $output .=
957                        $field{'name'} . q{: }
958                      . $lang_ext{'option_does_not_exist'}
959                      . "<br />\n";
960                }
961                next;
962
963            }
964            elsif ( $field{'type'} eq 'date' && $value ne q{} ) {
965                if ( $value !~ /[0-9]/xsm ) {
966                    $output .=
967                        $field{'name'} . q{: }
968                      . $lang_ext{'not_numeric'}
969                      . "<br />\n";
970                }
971                if ( $key eq 'ext_' . $id . '_day' ) {
972                    if ( $value < 1 ) {
973                        $output .=
974                            $field{'name'} . q{: }
975                          . $lang_ext{'too_small'}
976                          . "<br />\n";
977                    }
978                    if ( $value > 31 ) {
979                        $output .=
980                            $field{'name'} . q{: }
981                          . $lang_ext{'too_big'}
982                          . "<br />\n";
983                    }
984                    if ( length($value) == 1 ) {
985                        $newprofile{ 'ext_' . $id . '_day' } = '0' . $value;
986                    }
987                }
988                elsif ( $key eq 'ext_' . $id . '_month' ) {
989                    if ( $value < 1 ) {
990                        $output .=
991                            $field{'name'} . q{: }
992                          . $lang_ext{'too_small'}
993                          . "<br />\n";
994                    }
995                    if ( $value > 12 ) {
996                        $output .=
997                            $field{'name'} . q{: }
998                          . $lang_ext{'too_big'}
999                          . "<br />\n";
1000                    }
1001                    if ( length($value) == 1 ) {
1002                        $newprofile{ 'ext_' . $id . '_month' } = '0' . $value;
1003                    }
1004                }
1005                elsif ( $key eq 'ext_' . $id . '_year' ) {
1006                    if ( length($value) != 4 ) {
1007                        $output .=
1008                            $field{'name'} . q{: }
1009                          . $lang_ext{'invalid_year'}
1010                          . "<br />\n";
1011                    }
1012                }
1013                $newprofile{ 'ext_' . $id } =
1014                    $newprofile{ 'ext_' . $id . '_month' } . q{/}
1015                  . $newprofile{ 'ext_' . $id . '_day' } . q{/}
1016                  . $newprofile{ 'ext_' . $id . '_year' };
1017                if ( $newprofile{ 'ext_' . $id } !~ /^\d\d\/\d\d\/\d\d\d\d$/sm )
1018                {
1019                    $newprofile{ 'ext_' . $id } = q{};
1020                }
1021                next;
1022
1023            }
1024            elsif ( $field{'type'} eq 'checkbox' ) {
1025                if   ( $value ne q{} ) { $newprofile{ 'ext_' . $id } = 1; }
1026                else                   { $newprofile{ 'ext_' . $id } = 0; }
1027                next;
1028
1029            }
1030            elsif ( $field{'type'} eq 'email' && $value ne q{} ) {
1031                $value = substr $value, 0, $ext_max_email_length;
1032
1033                # uses the code from Profile.pm without further checking...
1034                if ( $value !~ /[\w\-\.\+]+\@[\w\-\.\+]+\.(\w{2,4}$)/sm ) {
1035                    $output .=
1036                        $field{'name'} . q{: }
1037                      . $lang_ext{'invalid_char'}
1038                      . "<br />\n";
1039                }
1040                if (
1041                    ( $value =~ /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)|(\.$)/sm )
1042                    || ( $value !~
1043                        /^.+@\[?(\w|[-.])+\.[a-zA-Z]{2,4}|[0-9]{1,4}\]?$/sm )
1044                  )
1045                {
1046                    $output .=
1047                        $field{'name'} . q{: }
1048                      . $lang_ext{'invalid_char'}
1049                      . "<br />\n";
1050                }
1051
1052            }
1053            elsif ( $field{'type'} eq 'url' && $value ne q{} ) {
1054                $value = substr $value, 0, $ext_max_url_length;
1055
1056            }
1057            elsif ($field{'type'} eq 'image'
1058                && $value ne q{}
1059                && $value ne 'http://' )
1060            {
1061                $value = substr $value, 0, $ext_max_image_length;
1062                @options = split /\^/xsm, $field{'options'};
1063                if ( $options[2] ne q{} ) {
1064                    @allowed_extensions = split / /sm, $options[2];
1065                    $match = 0;
1066                    foreach my $extension (@allowed_extensions) {
1067                        if ( grep { /$extension$/ism } $value ) {
1068                            $match = 1;
1069                            last;
1070                        }
1071                    }
1072                    if ( $match == 0 ) {
1073                        $output .=
1074                            $field{'name'} . q{: }
1075                          . $lang_ext{'invalid_extension'}
1076                          . "<br />\n";
1077                    }
1078                }
1079
1080                # filename check from Profile.pm:
1081                if ( $value !~
1082                    m{\A[0-9a-zA-Z_\.\#\%\-\:\+\?\$\&\~\.\,\@/]+\Z}sm )
1083                {
1084                    $output .=
1085                        $field{'name'} . q{: }
1086                      . $lang_ext{'invalid_char'}
1087                      . "<br />\n";
1088                }
1089            }
1090            $newprofile{ 'ext_' . $id } = $value;
1091        }
1092    }
1093
1094# check if required fields are filled and add missing fields to $newprofile, just to be on the safe side
1095    $id = 0;
1096    foreach (@ext_prof_fields) {
1097        ext_get_field($id);
1098        $value = ext_get( $pusername, $field{'name'}, 1 );
1099        if ( defined $newprofile{ 'ext_' . $id } ) {
1100            if (   $field{'type'} eq 'checkbox'
1101                || $field{'type'} eq 'radiobuttons' ) {
1102                if ( $newprofile{ 'ext_' . $id } eq q{} ) {
1103                    $newprofile{ 'ext_' . $id } = 0;
1104                }
1105            }
1106            elsif ( $field{'type'} eq 'select' ) {
1107                if ( $newprofile{ 'ext_' . $id } eq q{} ) {
1108                    $newprofile{ 'ext_' . $id } = 0;
1109                }
1110                @options = split /\^/xsm, $field{'options'};
1111                if ( $options[ $newprofile{ 'ext_' . $id } ] eq q{ } ) {
1112                    $newprofile{ 'ext_' . $id } = q{};
1113                }
1114            }
1115            elsif ( $field{'type'} eq 'image' ) {
1116                if ( $newprofile{ 'ext_' . $id } eq 'http://' ) {
1117                    $newprofile{ 'ext_' . $id } = q{};
1118                }
1119            }
1120        }
1121
1122        # load old settings which where invisible/restricted
1123        if ( $action eq 'register2' ) {
1124            if (   $field{'editable_by_user'} == 0
1125                && $field{'required_on_reg'} == 0 )
1126            {
1127                $newprofile{ 'ext_' . $id } = $value;
1128            }
1129        }
1130        else {
1131            if (   $field{'editable_by_user'} == 0
1132                && !$iamadmin
1133                && ( !$iamgmod || !$allow_gmod_profile ) )
1134            {
1135                $newprofile{ 'ext_' . $id } = $value;
1136            }
1137        }
1138
1139        # if setting didn't get submitted or field is disabled, load old value
1140        if (   !defined $newprofile{ 'ext_' . $id }
1141            && $field{'active'} == 0
1142            && $action eq 'register2' )
1143        {
1144            $newprofile{ 'ext_' . $id } = 0;
1145        }
1146        elsif ( !defined $newprofile{ 'ext_' . $id } || $field{'active'} == 0 )
1147        {
1148            $newprofile{ 'ext_' . $id } = $value;
1149        }
1150
1151#       if (!defined $newprofile{'ext_'.$id} || $field{'active'} == 0) { $newprofile{'ext_'.$id} = $value; }
1152        if (   $field{'required_on_reg'} == 1
1153            && $newprofile{ 'ext_' . $id } eq q{}
1154            && $action eq 'register2' )
1155        {
1156            $output .=
1157              $field{'name'} . q{: } . $lang_ext{'required'} . "<br />\n";
1158        }
1159
1160        # only fill with default value AFTER check of requirement
1161        if ( $field{'type'} eq 'text' && $newprofile{ 'ext_' . $id } eq q{} ) {
1162            @options = split /\^/xsm, $field{'options'};
1163            if ( $options[3] ne q{} ) {
1164                $newprofile{ 'ext_' . $id } = $options[3];
1165            }
1166        }
1167        elsif ( $field{'type'} eq 'spacer' ) {
1168            $newprofile{ 'ext_' . $id } = q{};
1169        }
1170        elsif ($field{'type'} eq 'select'
1171            && $newprofile{ 'ext_' . $id } eq q{} )
1172        {
1173            $newprofile{ 'ext_' . $id } = 0;
1174        }
1175        $id++;
1176    }
1177
1178# write our now validated profile information back into the usually used variable
1179    %FORM = %newprofile;
1180
1181    return $output;
1182}
1183
1184# stores the submitted profile on disk
1185sub ext_saveprofile {
1186    my ( $pusername, $id, %newprofile, @fields ) = ( shift, 0, %FORM );
1187
1188    # note: we expect the new profile to be complete and validated already
1189
1190    foreach (@ext_prof_fields) {
1191        ${ $uid . $pusername }{ 'ext_' . $id } = $newprofile{ 'ext_' . $id };
1192        $id++;
1193    }
1194    return;
1195}
1196
11971;
1198
1199# file formats used by this code:
1200#
1201#  username.vars - contains the additional user profile information. Number is field-id
1202#  -------------
1203#  ...
1204#  'ext_0',"value"
1205#  'ext_1',"value"
1206#  'ext_2',"value"
1207#  ...
1208#
1209#  @ext_prof_order - contains the order in which the fields will be displayed
1210#  ---------------------------
1211#  ("name","name","name",....)
1212#
1213#  extended_profiles_fields.txt - defines the new profile fields. Uses line number as field-id
1214#  ----------------------------
1215#  ("name|type|options|active|comment|required_on_reg|visible_in_viewprofile|v_users|v_groups|visible_in_posts|p_users|p_groups|p_displayfieldname|visible_in_memberlist|m_users|m_groups|editable_by_user|visible_in_posts_popup|pp_users|pp_groups|pp_displayfieldname","name|type|options|active|comment|required_on_reg|visible_in_viewprofile|v_users|v_groups|visible_in_posts|p_users|p_groups|p_displayfieldname|visible_in_memberlist|m_users|m_groups|editable_by_user|visible_in_posts_popup|pp_users|pp_groups|pp_displayfieldname","name|type|options|active|comment|required_on_reg|visible_in_viewprofile|v_users|v_groups|visible_in_posts|p_users|p_groups|p_displayfieldname|visible_in_memberlist|m_users|m_groups|editable_by_user|visible_in_posts_popup|pp_users|pp_groups|pp_displayfieldname",....)
1216#
1217#  Here are all types with their possible type-specific options. If options contain multiple entries, separated by ^
1218#  - text       limit_len^width^is_numberic^default_value^allow_ubbc
1219#  - text_multi     limit_len^rows^cols^allow_ubbc
1220#  - select     option1^option2^option3... (first option is default)
1221#  - radiobuttons   option1^option2^option3... (first option is default)
1222#  - spacer     br_or_hr^visible_in_editprofile
1223#  - checkbox       -
1224#  - date       -
1225#  - emial      -
1226#  - url        -
1227#  - image      width^height^allowed_extensions
1228#
1229#  required_on_reg can have value 0 (disabled), 1 (required on registration) and 2 (not req. but display on reg. page anyway)
1230#  editable_by_user can have value 0 (will only show on the "admin edits" page), 1 ("edit profile" page), 2 ("contact information" page), 3 ("Options" page) and 4 ("PM Preferences" page)
1231#  allowed_extensions is a space-seperated list of file extensions, example: "jpg jpeg gif bmp png"
1232#  v_groups, p_groups, m_groups, pp_groups format: "Administrator" or "Moderator" or "Global Moderator" or NoPost{...} or Post{...}
1233#
1234# NOTE: use prefix "ext_" in sub-, variable- and formnames to prevent conflicts with other mods
1235#
1236# easy mod integration: use &ext_get($username,"fieldname") go get user's field value
1237#
1238###############################################################################
1239