4 use vars qw(%GET %POST $title $body $head @errors);
7 LJ::set_active_crumb('manageuserpics');
8 BML::set_language_scope("/editpics.bml");
11 if (LJ::Request->uri =~ /editpics-beta/) {
15 $title = $ML{'.title3'};
20 $title = $ML{'Error'};
21 $body = LJ::bad_input(@_);
25 unless (LJ::text_in(\%POST)) {
26 return $err->("Invalid UTF-8 Input");
29 my $remote = LJ::get_remote();
31 $body = "<?needlogin?>";
35 if ($remote->underage) {
36 return BML::redirect("$LJ::SITEROOT/agecheck/?s=1");
44 js/perlbal-uploadtrack.js
48 my $authas = $GET{'authas'} || $remote->{'user'};
49 my $u = LJ::get_authas_user($authas) or
50 return $err->($ML{'error.invalidauth'});
52 # extra arguments for get requests
53 my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
55 my $returl = LJ::CleanHTML::canonical_url($POST{'ret'});
56 my $picurl = LJ::CleanHTML::canonical_url($POST{'urlpic'});
57 my $fotobilder = index($returl, $LJ::FB_SITEROOT) == 0 &&
58 $picurl =~ m!^$LJ::FB_SITEROOT/~?$remote->{'user'}/pic/!;
61 (LJ::check_referer($returl) || LJ::check_referer('/editpics.bml'))) {
63 return $err->('Invalid referring site or redirection not allowed')
64 unless $returl =~ /$LJ::FB_DOMAIN/ && LJ::get_cap($u, 'fb_account');
67 if (LJ::get_cap($u, "readonly")) {
68 $title = "Read-only mode";
69 $body = $LJ::MSG_READONLY_USER;
73 # update this user's activated pics
74 $u->activate_userpics;
76 # get userpics and count 'em
77 my @userpics = LJ::Userpic->load_user_userpics($u, { load_expunged => 1 });
79 # get maximum number of userpics and expiring dates of its for this user
81 my $max = LJ::get_cap($u, "userpics", \%cap_opts);
82 my $trim = sub { return substr($_[0], 0, 10); };
84 push @upics_str => BML::ml('pay.widget.userpics.exp.small', { num => $cap_opts{small_packs}, date => $trim->($cap_opts{small_exp})}) if $cap_opts{small_packs};
85 push @upics_str => BML::ml('pay.widget.userpics.exp.large', { num => $cap_opts{large_packs}, date => $trim->($cap_opts{large_exp})}) if $cap_opts{large_packs};
91 ### save changes to existing pics
92 if ($POST{'action:save'}) {
93 # form being posted isn't multipart, since we were able to read from %POST
94 unless (LJ::check_form_auth()) {
95 return $err->($ML{'error.invalidform'});
98 my @delete; # userpic objects to delete
103 # we need to count keywords based on what the user provided, in order
104 # to find duplicates. $up->keywords doesn't work, because re-using a
105 # keyword will remove it from the other userpic without our knowing
106 my $count_keywords = sub {
108 $used_keywords{$_}++ foreach split(/,\s*/, $kwlist);
111 foreach my $up (@userpics) {
115 if ($POST{"delete_$picid"}) {
120 # we're only going to modify keywords/comments on active pictures
121 if ($up->inactive || $up->expunged) {
122 # use 'orig' because we don't POST disabled fields
123 $count_keywords->($POST{"kw_orig_$picid"});
127 $count_keywords->($POST{"kw_$picid"});
129 # only modify if changing the data, make sure not collidiing with other edits, etc
130 if ($POST{"kw_$picid"} ne $POST{"kw_orig_$picid"}) {
131 my $kws = $POST{"kw_$picid"};
134 $up->set_keywords($kws);
135 } or push @errors, $@;
140 $up->set_comment ($POST{"com_$picid"})
141 unless $POST{"com_$picid"} eq $POST{"com_orig_$picid"};
142 } or push @errors, $@;
145 foreach my $kw (keys %used_keywords) {
146 next unless $used_keywords{$kw} > 1;
147 push @errors, BML::ml('.error.keywords', {ekw => $kw});
150 if (@delete && $LJ::DISABLE_MEDIA_UPLOADS) {
151 push @errors, $ML{'.error.nomediauploads.delete'};
158 foreach my $up (@delete) {
160 my $r = $up->delete();
169 LJ::run_hooks('set_userpics', $u, $cnt, 0);
171 # if any of the userpics they want to delete are active, then we want to
172 # re-run activate_userpics() - turns out it's faster to not check to
173 # see if we need to do this
174 $u->activate_userpics;
177 my $new_default = $POST{'defaultpic'}+0;
178 if ($POST{"delete_${new_default}"}) {
183 if ($new_default && $new_default != $u->{'defaultpicid'}) {
184 my ($up) = grep { $_->id == $new_default } @userpics;
186 # see if they are trying to make an inactive userpic their default
187 if ($up && !$up->inactive && !$up->expunged) {
190 } elsif ($new_default eq '0' && $u->{'defaultpicid'}) {
191 # selected the "no default picture" option
192 LJ::update_user($u, { defaultpicid => 0 });
195 # reload the pictures to account for deleted
196 @userpics = LJ::Userpic->load_user_userpics($u) if scalar @delete;
199 ### no post data, so we'll parse the multipart data
200 my $size = LJ::Request->header_in("Content-length");
202 return $err->("No content-length header: can't upload");
205 my $MAX_UPLOAD = LJ::Userpic->max_allowed_bytes($u);
208 # act like the factory is disabled if no mogile
209 my $factory_disabled = $LJ::DISABLED{'userpicfactory'} || !LJ::mogclient();
210 $MAX_UPLOAD *= 10 unless $factory_disabled;
212 ## User can upload image or set image's uri.
213 my $userpic_raw = '';
214 if ($POST{userpic} or $POST{src} eq "url"){
216 my ($size, $content) = (undef, undef);
218 return $err->($ML{'.error.badurl'})
219 if $POST{'src'} eq "url" && $POST{'urlpic'} !~ /^http:\/\//;
222 if ($POST{'userpic'} || $POST{'src'} eq "url") {
223 my $res = LJ::fetch_userpic(%POST, maxupload => $MAX_UPLOAD);
225 return $err->("No uploaded file")
226 if $res->{size} == -1;
228 return $err->($ML{'.error.urlerror'})
229 unless $res->{content};
231 $content = $res->{content};
232 $size = $res->{size};
235 ## do we get an image?
236 if (defined $size and $size > 0 and defined $content){
237 ## OK, verify its size and dimentions (maybe resize)
239 # If image is lagger than 100x100 we can resize it all
240 # or send user to /tools/userpicfactory.bml where user can
241 # select desired part of image
243 my ($imagew, $imageh, $filetype) = Image::Size::imgsize(\$content);
244 return $err->(BML::ml(".error.unsupportedtype", {filetype => $filetype}))
245 if not $imagew or not $imageh;
246 return $err->(BML::ml('.error.imagetoolarge', {imagesize => "${imagew}x$imageh"}))
247 if $imagew > 5000 or $imageh > 5000;
249 # Verify it's content.
252 LJ::run_hooks('check_img_content', \$error, $content, {
253 username => $u->{'user'}, # it can be not a $remote.
255 return $err->(BML::ml(".error.restrictedimage")) if $error;
258 ## resize or send user to factory.
259 if ($imagew > 100 or $imageh > 100 or $size > LJ::Userpic->max_allowed_bytes($u) + 2048){
260 ## Image has to be resized!
263 ## save image content to temporary file in MogileFS
264 ## than german worker or UserpicFatory handle it.
265 my $mog_fh = LJ::mogclient()->new_file("upf:" . $u->userid, 'temp');
266 return $err->("Userpic uploading is temporary disabled")
268 $mog_fh->print($content);
271 if ($factory_disabled){
272 # [ resized image data, MIME, w, h ]
273 my $picinfo = eval { LJ::get_upf_scaled( size => 100,
276 or return $err->("There was an error in generating the userpic: $@");
278 # we've got a resized image
279 $content = ${ $picinfo->[0] }; # update data
282 # Send user to Factory Tool
283 return BML::redirect("$LJ::SITEROOT/tools/userpicfactory.bml?keywords=" .
284 BML::eurl($POST{'keywords'}) . "&comments=" .
285 BML::eurl($POST{'comments'}) . "&imageWidth=${imagew}&imageHeight=${imageh}".
286 "&make_default=" . BML::eurl($POST{'make_default'}) . '&authas=' .
287 BML::eurl($GET{'authas'}) . '&sfx=' . BML::eurl($suffix));
291 ## put verified content as ready to create Userpic
292 $userpic_raw = $content;
295 } elsif ($POST{'src'} eq "factory") {
298 my $scaledsizemax = $POST{'scaledSizeMax'};
299 my $x1 = $POST{'x1'}+0;
300 my $x2 = $POST{'x2'}+0;
301 my $y1 = $POST{'y1'}+0;
302 my $y2 = $POST{'y2'}+0;
304 return $err->("Invalid userpic creation parameters.") if (!$scaledsizemax || !$x2);
312 border => $POST{'border'},
316 or return $err->("There was an error in generating the userpic: $@");
318 $userpic_raw = ${ $picinfo->[0] };
323 ## Are there any image data that ready to be a.... USERPIC?!
325 # pam-pam-pam.... ouuhhh...
326 my $userpic = eval { LJ::Userpic->create($u, data => \$userpic_raw); };
329 return $err->($@->as_string);
332 ## Yaouhuuuu... we've got a new userpic... lalala.
333 my $picid = $userpic->id;
335 push @info, "Your userpic has been successfully uploaded";
337 # make it their default pic?
338 if ($POST{'make_default'}) {
339 $userpic->make_default;
342 $userpic->set_keywords($POST{'keywords'}) if $POST{'keywords'};
343 $userpic->set_comment($POST{'comments'}) if $POST{'comments'};
344 $userpic->set_fullurl($POST{'url'}) if $POST{'url'};
346 # redirect back to ourselves/fotobilder
347 $returl = LJ::CleanHTML::canonical_url($POST{'ret'});
349 my ($redir_host) = $returl =~ m!^http://([\.:\w-]+)!i;
350 return BML::redirect($returl) if $LJ::REDIRECT_ALLOWED{$redir_host};
353 # yey we created a new pic, reload the @userpics
354 @userpics = LJ::Userpic->load_user_userpics($u);
358 # now fall through to edit page and show the updated userpic info
361 if ($fotobilder && $POST{'md5sum'}) {
362 $fotobilder = 0 if LJ::Userpic->new_from_md5($u, $POST{'md5sum'});
366 # authas switcher form
367 $body .= "<form method='get' id='userpic_authas' action='editpics$suffix.bml'>\n";
368 $body .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} }) . "\n";
369 $body .= "</form>\n\n";
372 # if we're disabling media, say so
373 $body .= "<?warningbar $ML{'error.mediauploadsdisabled'} warningbar?>"
374 if $LJ::DISABLE_MEDIA_UPLOADS;
377 $body .= '<div class="warningbar">';
378 $body .= "<div>$_</div>" foreach @info;
382 # print out upload pic box
383 my $emit_upload_box = sub {
384 $body .= '<a name="uploadBox"></a>';
385 # if (scalar @userpics < $max) {
387 # upload form (with perlbal upload-tracking)
389 <iframe name='upiframe' width='1' height='1' style='border: none'></iframe>
390 <div id='uploadBox' class='pkg};
391 if (scalar @userpics >= $max) { $body .=' st-disabled'; }
393 '><div id='uploadBox-inner'>
394 <form enctype="multipart/form-data" action="editpics$suffix.bml$getextra" method='post' id='uploadPic'>
395 <input type="hidden" id="go_to" name="go_to" value="editpics$suffix.bml$getextra" />
398 $body .= "<?h1 $ML{'.uploadheader.fb'} h1?>\n";
399 $body .= "<?p " . BML::ml('.uploaddesc.fb', {'aopts' => "href='$LJ::FB_SITEROOT'", 'sitename' => $LJ::FB_SITENAME}) . " p?>\n\n";
401 $body .= "<?h1 $ML{'.uploadheader'} h1?>\n";
402 $body .= "<p class='detail'><a href='javascript:void(0)' onclick='toggleElement(\"upload_desc\")' id='upload_desc_link'>About Userpics</a></p>";
403 $body .= "<div id='upload_desc'>";
404 $body .= "<?p $ML{'.uploaddesc'} p?>\n";
405 $body .= "</div><!-- end #uploaddesc -->";
408 $body .= "<img src='$picurl' />";
409 my $url = LJ::CleanHTML::canonical_url($POST{'url'});
410 $body .= LJ::html_hidden('src', 'url', 'urlpic', $picurl, 'url', $url, 'ret' => $returl);
412 $body .= "<div id='upload_wrapper' class='pkg'>\n";
413 $body .= "<p class='pkg'>\n";
414 $body .= LJ::html_check({ 'type' => 'radio', 'name' => 'src', 'id' => 'radio_file',
415 'class' => 'radio', 'value' => 'file', 'selected' => '1',
416 'accesskey' => $ML{'.fromfile.key'} }) . "\n";
417 $body .= "<label for='radio_file'>$ML{'.fromfile'}</label><br />\n";
418 $body .= "<input type='file' class='file' name='userpic' size='22'";
419 if (scalar @userpics >= $max) {
420 $body .= "disabled='disabled' ";
422 $body .= " style='margin: 0em 0em 0.5em 2em;' />\n";
424 $body .= "<p class='pkg'>\n";
425 $body .= LJ::html_check({ 'type' => 'radio', 'name' => 'src', 'value' => 'url',
426 'id' => 'radio_url', 'class' => 'radio', 'accesskey' => $ML{'.fromurl.key'} }) . "\n";
427 $body .= "<label for='radio_url'>$ML{'.fromurl'}</label><br />\n";
428 $body .= LJ::html_text({ 'name' => 'urlpic', class => 'text', style => 'margin: 0em 0em 0.5em 2em;', disabled => scalar @userpics >= $max }) . "\n";
430 $body .= "<p class='detail'>$ML{'.label.formats.desc'}</p>\n";
434 $body .= "<hr class='hr' />";
436 $body .= "<p class='pkg'>\n";
437 $body .= "<label class='left' for='keywords'>$ML{'.label.keywords'}</label>\n";
438 $body .= "<span class='input-wrapper'>";
439 $body .= LJ::html_text({ 'name' => 'keywords', class => 'text', id => 'keywords', disabled => scalar @userpics >= $max }) . "\n";
440 $body .= LJ::help_icon_html('upic_keywords') . "\n";
443 $body .= "<p class='detail'>$ML{'.label.keywords.desc'}</p>\n";
445 if (LJ::Userpic->user_supports_comments($u)) {
446 $body .= "<p class='pkg'>\n";
447 $body .= "<label class='left' for='comments'>$ML{'.label.comment'}</label>\n";
448 my $comments = $fotobilder ? $POST{'comments'} : undef;
449 $body .= "<span class='input-wrapper'>";
450 $body .= LJ::html_text({ 'name' => 'comments', 'class' => 'text', id => 'comments', 'maxlength' => LJ::CMAX_UPIC_COMMENT, 'value', $comments, disabled => scalar @userpics >= $max }) . "\n";
451 $body .= LJ::help_icon_html('upic_comments') . "\n";
454 $body .= "<p class='detail'>$ML{'.label.comment.desc'}</p>\n";
457 $body .= "<p class='pkg'>\n";
458 $body .= LJ::html_check({ 'type' => 'checkbox',
459 'name' => 'make_default',
460 'id' => 'make_default',
462 'selected' => @userpics ? 0 : 1,
463 'disabled' => scalar @userpics >= $max,
464 'accesskey' => $ML{'.makedefault.key'} });
466 $body .= "<label for='make_default'>$ML{'.makedefault'}</label>\n";
469 $body .= "<p class='pkg' id='submit_wrapper'>";
470 $body .= LJ::html_submit(undef, $ML{'.btn.proceed'},
471 { disabled => $LJ::DISABLE_MEDIA_UPLOADS });
475 </div><div class="b-blocker b-blocker-white"></div></div><!-- end #uploadBox -->
476 <div id="uploadStatus" style="display: none;"></div>
477 <div id="progressBar" style="display: none;"></div>
481 if (scalar @userpics >= $max) {
482 $body .= "<div id='limit'>\n";
484 if ($inline .= LJ::run_hook("cprod_inline", $u, 'EditPicsMax')) {
487 $body .= BML::ml('.error.toomanypics_standout', { num => $max });
489 $body .= "</div><!-- end #limit -->";
494 $emit_upload_box->();
496 # print out each pic and editing fields
497 if (scalar @userpics && !$fotobilder) {
498 $body .= "<div id='current_userpics'>";
499 $body .= "<form method='post' action='editpics$suffix.bml$getextra'>";
500 $body .= LJ::form_auth();
503 <?h1 $ML{'.curpics'} h1?>
504 <?p $ML{'.curpics.desc2'} p?>
505 <div class='EditPicsStatus'>
508 $body .= "<p><strong>" . BML::ml('.piclimitstatus', {current => scalar @userpics, max => $max}) . "</strong>";
509 $body .= " (" . (join ';', @upics_str) . ")" if @upics_str;
511 if (scalar @userpics >= $max) {
513 if ($inline .= LJ::run_hook("cprod_inline", $u, 'EditPics')) {
514 $body .= "<?warningbar" . $inline . "warningbar?>";
516 $body .= "<p>".BML::ml('cprod.editpics.text7.v1',{ "num" => $max })."</p>";
521 $body .= "<div id='list_userpics' style='width: 100%; float: left;'>";
522 foreach my $pic (@userpics) {
524 my $url = "$LJ::USERPIC_ROOT/$pid/$u->{'userid'}";
525 my $extra_class = "";
527 if ($pic->expunged) {
528 $url .= "?viewall=1";
529 $extra_class = " EditPicsUserpicSuspended";
531 $body .= "<div class='pkg userpic_wrapper'>";
532 $body .= "<span class='EditPicsUserpic$extra_class'><img src='$url' width='$pic->{'width'}' height='$pic->{'height'}' /></span>\n";
534 # TODO: if no keywords then light grey text and empty out when you click in it
535 my $keywords = $pic->keywords(raw => 1);
536 my $comment = $pic->comment;
537 $body .= "<div class='userpic_controls' style='float: left;'>";
538 $body .= "<div class='userpic_keywords pkg'>\n";
539 $body .= "<label class='left' for='kw_$pid'>$ML{'.label.keywords'}</label>\n ";
540 $body .= LJ::html_text({'name' => "kw_$pid", 'class' => "text", 'id' => "kw_$pid",
541 'value' => $keywords,
542 'disabled' => $pic->inactive || $pic->expunged }) . "\n";
543 $body .= LJ::html_hidden({ 'name' => "kw_orig_$pid",
544 'value' => $keywords }) . "\n";
547 if ($pic->supports_comments) {
548 $body .= "<div class='userpic_comments pkg'>\n";
549 $body .= "<label class='left' for='com_$pid'>$ML{'.label.comment'}</label>\n ";
550 $body .= LJ::html_text({ 'name' => "com_$pid", 'class' => "text", 'id' => "com_$pid",
552 'maxlength' => LJ::CMAX_UPIC_COMMENT,
553 'disabled' => $pic->inactive || $pic->expunged }) . "\n";
554 $body .= LJ::html_hidden({ 'name' => "com_orig_$pid",
555 'value' => $comment }) . "\n";
560 $body .= "<div class='userpic_defaultdelete pkg'>";
561 $body .= LJ::html_check({ 'type' => 'radio', 'name' => 'defaultpic', 'class' => "radio", 'value' => $pid,
562 'selected' => $pic->is_default ? 1 : 0,
564 'disabled' => $pic->inactive || $pic->expunged});
565 $body .= "<label class='userpic_default_label' for='def_$pid'>$ML{'.label.default'}</label> ";
566 $body .= LJ::html_check({ 'type' => 'checkbox', 'name' => "delete_$pid", 'class' => "checkbox",
567 'id' => "del_$pid", 'value' => 1,
568 'disabled' => $LJ::DISABLE_MEDIA_UPLOADS });
569 $body .= "<label for='del_$pid'>$ML{'.label.delete'}</label>";
570 if ($pic->inactive) {
571 $body .= " <i>[$ML{'userpic.inactive'}]</i> " . LJ::help_icon('userpic_inactive');
573 if ($pic->expunged) {
574 $body .= " <i>[$ML{'userpic.suspended'}]</i> " . LJ::help_icon('userpic_suspended');
581 $body .= "<hr class='hr' />";
584 $body .= "</div><!-- end #list_userpics -->";
586 $body .= "<p id='no_default_userpic'>";
587 $body .= LJ::html_check({ 'name' => 'defaultpic',
591 'selected' => $u->{'defaultpicid'} == 0,
592 'raw' => "id='nodefpic'" });
593 $body .= "<label for='nodefpic'>$ML{'.nodefault'}</label></p>";
594 $body .= "<?standout" . LJ::html_submit('action:save', $ML{'.btn.save'}) . "standout?>";
596 $body .= "</div><!-- end #current_userpics -->";
597 $body .= "<script type='text/javascript'>\n";
598 $body .= "editpicsInit();\n";
599 $body .= "</script>\n";
601 } elsif (!$fotobilder) {
603 <?h1 $ML{'.nopics'} h1?>
604 <?p $ML{'.noneupload2'} p?>
612 head=><?_code return $head; _code?>
613 title=><?_code return $title; _code?>
614 body=><?_code return $body; _code?>
616 link: htdocs/login.bml, htdocs/allpics.bml
617 post: htdocs/editpics.bml