<?php
/**
* This file implements the UI view for the user picture crop form.
*
* This file is part of the evoCore framework - {@link http://evocore.net/}
* See also {@link https://github.com/b2evolution/b2evolution}.
*
* @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
*
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
*
* @package admin
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
global $display_mode, $Settings;
/**
* @var instance of User class
*/
global $edited_User;
/**
* @var current action
*/
global $action;
/**
* @var user permission, if user is only allowed to edit his profile
*/
global $user_profile_only;
/**
* @var the action destination of the form (NULL for pagenow)
*/
global $form_action;
/**
* @var instance of User class
*/
global $current_User;
/**
* @var File that should be cropped
*/
global $cropped_File;
global $aspect_ratio, $content_width, $content_height;
$aspect_ratio = param( 'aspect_ratio', 'double' );
$content_width = param( 'content_width', 'integer' );
$content_height = param( 'content_height', 'integer' );
$original_image_size = explode( 'x', $cropped_File->get_image_size() );
$image_height = $original_image_size[1];
$image_width = $original_image_size[0];;
$min_avatar_size = $Settings->get( 'min_picture_size' );
$can_crop = ( $image_height >= $min_avatar_size && $image_width >= $min_avatar_size
&& ! ( $image_height == $min_avatar_size && $image_width == $min_avatar_size ) );
if( $display_mode != 'js' )
{
// ------------------- PREV/NEXT USER LINKS -------------------
user_prevnext_links( array(
'user_tab' => 'avatar'
) );
// ------------- END OF PREV/NEXT USER LINKS -------------------
}
$Form = new Form( $form_action, 'user_checkchanges' );
if( is_admin_page() )
{
$ctrl_param = '?ctrl=user&user_tab=avatar&user_ID='.$edited_User->ID;
$form_title = '';
$form_class = 'fform';
}
else
{
global $Collection, $Blog;
$form_title = '';
$form_class = 'bComment';
$ctrl_param = url_add_param( $Blog->gen_blogurl(), 'disp='.$disp );
}
if( $display_mode != 'js' && is_admin_page() )
{
if( !$user_profile_only )
{
echo_user_actions( $Form, $edited_User, $action );
}
$form_text_title = TB_( 'Crop profile picture' ); // used for js confirmation message on leave the changed form
$form_title = get_usertab_header( $edited_User, '', $form_text_title );
}
$cropped_image_tag = $cropped_File->get_tag( '', '', '', '', 'original', '', '', '', '', '', '', '', '', '', 'none' );
// Display this error when JS is not enabled
echo '<noscript>'
.'<p class="error text-danger">'.TB_('Please activate Javascript in your browser in order to use this feature.').'</p>'
.'<style type="text/css">form#user_checkchanges { display:none }</style>'
.'</noscript>';
$Form->begin_form( $form_class, $form_title, array( 'title' => ( isset( $form_text_title ) ? $form_text_title : $form_title ) ) );
if( is_admin_page() )
{
$Form->hidden_ctrl();
}
else
{
$Form->hidden( 'disp', $disp );
$Form->hidden( 'action', $action );
}
$Form->add_crumb( 'user' );
$Form->hidden( 'user_tab', param( 'user_tab_from', 'string', 'avatar' ) );
$Form->hidden( 'user_ID', isset( $edited_User ) ? $edited_User->ID : $current_User->ID );
$Form->hidden( 'file_ID', $cropped_File->ID );
$Form->hidden( 'image_crop_data', '' );
if( isset( $Blog ) )
{
$Form->hidden( 'blog', $Blog->ID );
}
$close_icon = '';
if( $display_mode == 'js' )
{ // Display a close link for popup window
$close_icon = action_icon( TB_('Close this window'), 'close', '', '', 0, 0, array( 'id' => 'close_button', 'class' => 'floatright' ) );
}
// Start displaying content
if( ! $can_crop )
{
echo '<div style="height: '.$content_height.'px; width: '. $content_width.'px; position=relative; text-align: center;">';
echo '<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">'.sprintf( TB_('Only images larger than %dx%d pixels can be cropped.'), $min_avatar_size, $min_avatar_size ).'</div>';
echo '</div>';
}
else
{
echo '<div id="user_crop_content" style="height: '.$content_height.'px; width: '. $content_width.'px;">';
echo '</div>';
$Form->button( array( 'type' => 'submit', 'name'=>'actionArray[crop]', 'value'=> TB_('Apply'), 'class' => 'SaveButton btn-primary' ) );
}
$Form->end_form();
if( $can_crop )
{
?>
<style>
#user_crop_workarea img {
object-fit: contain;
width: <?php echo $content_width;?>;
height: <?php echo $content_height;?>;
visibility: hidden;
}
div.jcrop-holder {
margin: 0 auto;
position: relative;
top: 50%;
transform: translateY(-50%);
}
</style>
<script>
var jcrop_api;
var image_url = '<?php echo format_to_js( $cropped_File->get_url() ); ?>';
var gutter = 10;
var padding = 0;
var content_height = <?php echo $content_height;?>;
var content_width = <?php echo $content_width;?>;
var show_large_preview = true;
var show_small_preview = true;
var large_preview_size = 128;
var small_preview_size = 64;
var preview_size;
var preview_margin = 10;
var preview_orientation = 'portrait';
var aspect_ratio = <?php echo $aspect_ratio;?>;
var render_mode = 'landscape';
var workarea_height;
var workarea_width;
var workarea_aspect_ratio;
var original_image_height = <?php echo $image_height;?>;
var original_image_width = <?php echo $image_width;?>;
var original_image_aspect_ratio = original_image_height / original_image_width;
var size_ratio = 1;
var working_image_height = null;
var working_image_width = null;
var working_image_aspect_ratio = null;
var initial_crop_selection = [];
//console.debug( 'Content: ', content_width, content_height );
//console.debug( 'Original Image: ', original_image_width, original_image_height );
function get_working_image_dimensions( w_height, w_width, image_aspect_ratio )
{ // We'll use this function to determine which mode will provide the larger working image
var w_aspect_ratio = w_height / w_width;
if( w_aspect_ratio > image_aspect_ratio )
{ // width is limiting dimension
i_width = w_width;
i_height = w_width * original_image_aspect_ratio;
}
else
{ // height is limiting dimension
i_height = w_height;
i_width = w_height / original_image_aspect_ratio;
}
i_area = i_width * i_height;
return { height: i_height, width: i_width, area: i_area };
}
function init_layout()
{
var lw_width, lw_height, lw_area, lw_aspect_ratio;
var pw_width, pw_height, pw_area, pw_aspect_ratio;
var i_width, i_height, i_area;
if( aspect_ratio < 1 && ( large_preview_size > ( content_width / 3 ) ) || aspect_ratio > 1 && ( large_preview_size > ( content_height / 3 ) ) )
{
preview_size = small_preview_size;
}
else
{
preview_size = large_preview_size;
}
// Try landscape mode first
lw_width = content_width - preview_size - gutter - ( padding * 2 );
lw_height = content_height - ( padding * 2 );
lw_area = lw_width * lw_height;
lw_aspect_ratio = lw_height / lw_width;
var l_view = get_working_image_dimensions( lw_height, lw_width, original_image_aspect_ratio );
// Now let's try portrait mode
pw_height = content_height - preview_size - gutter - ( padding * 2 );
pw_width = content_width - ( padding * 2 );
pw_area = pw_width * pw_height;
pw_aspect_ratio = pw_height / pw_width;
var p_view = get_working_image_dimensions( pw_height, pw_width, original_image_aspect_ratio );
// See what mode provides the largest working image
if ( l_view.area > p_view.area )
{
render_mode = 'landscape';
}
else
{
render_mode = 'portrait';
}
// Determine if we can show all the preview images
if( render_mode == 'portrait' )
{
if( ( content_width < original_image_width ? content_width : original_image_width ) > ( ( ( large_preview_size + preview_margin ) * 2 ) + ( ( small_preview_size + preview_margin ) * 2 ) ) )
{
show_large_preview = true;
show_small_preview = true;
preview_size = large_preview_size;
}
else if( ( ( content_width < original_image_width ? content_width : original_image_width ) > ( ( large_preview_size + preview_margin ) * 2 ) ) && ( ( 0.25 * content_height ) >= large_preview_size ) )
{
show_large_preview = true;
show_small_preview = false;
preview_size = large_preview_size;
}
else if( ( ( content_width < original_image_width ? content_width : original_image_width ) > ( ( small_preview_size + preview_margin ) * 2 ) ) && ( ( 0.25 * ( render_mode == 'landscape' ? content_width : content_height ) ) >= small_preview_size ) )
{
show_large_preview = false;
show_small_preview = true;
preview_size = small_preview_size;
}
else
{
show_large_preview = false;
show_small_preview = false;
preview_size = 0;
}
}
else
{
if( ( content_height < original_image_height ? content_height : original_image_height ) > ( ( ( large_preview_size + preview_margin ) * 2 ) + ( ( small_preview_size + preview_margin ) * 2 ) ) )
{
show_large_preview = true;
show_small_preview = true;
preview_size = large_preview_size;
}
else if( ( ( content_height < original_image_height ? content_height : original_image_height ) > ( ( large_preview_size + preview_margin ) * 2 ) ) && ( ( 0.25 * ( content_width < content_height ? content_width : content_height ) ) >= large_preview_size ) )
{
show_large_preview = true;
show_small_preview = false;
preview_size = large_preview_size;
}
else if( ( ( content_height < original_image_height ? content_height : original_image_height ) > ( ( small_preview_size + preview_margin ) * 2 ) ) && ( ( 0.25 * ( content_width < content_height ? content_width : content_height ) ) >= small_preview_size ) )
{
show_large_preview = false;
show_small_preview = true;
preview_size = small_preview_size;
}
else if( ( content_height < original_image_height ? content_height : original_image_height ) > ( ( small_preview_size ) )
&& ( ( content_width ) > ( lw_width + ( small_preview_size * 2 ) ) ) )
{ // This is a special case when there is not enough room for a vertical preview but a horizontal preview can be accomodated
show_large_preview = false;
show_small_preview = true;
preview_size = small_preview_size;
preview_orientation = 'landscape';
}
else
{
show_large_preview = false;
show_small_preview = false;
preview_size = 0;
}
}
//console.debug( 'Render mode: ', render_mode );
//console.debug( 'Show large preview: ', show_large_preview );
//console.debug( 'Show small preview: ', show_small_preview );
//console.debug( 'Preview Size: ', preview_size );
}
function init_workarea()
{
if( render_mode == 'portrait' )
{
workarea_height = content_height - preview_size - gutter - ( padding * 2 );
workarea_width = content_width - ( padding * 2 );
}
else
{
workarea_height = ( content_height - ( padding * 2 ) );
workarea_width = content_width - preview_size - gutter - ( padding * 2 );
}
workarea_aspect_ratio = ( workarea_height / workarea_width );
//console.debug( 'Workarea: ', workarea_width, workarea_height );
}
function init_working_image()
{
if( workarea_aspect_ratio == original_image_aspect_ratio )
{
working_image_height = workarea_height;
working_image_width = workarea_width;
size_ratio = original_image_height / working_image_width;
//console.debug( 'Limiting dimension: ', 'both' );
}
else if( workarea_aspect_ratio > original_image_aspect_ratio )
{
working_image_width = workarea_width;
working_image_height = workarea_width * original_image_aspect_ratio;
size_ratio = original_image_width / working_image_width;
//console.debug( 'Limiting dimension: ', 'width' );
}
else
{
working_image_height = workarea_height;
working_image_width = workarea_height / original_image_aspect_ratio;
size_ratio = original_image_height / working_image_height;
//console.debug( 'Limiting dimension: ', 'height' );
}
// Should be always equal to original image aspect ratio
working_image_aspect_ratio = working_image_height / working_image_width;
//console.debug( 'Image: ', working_image_width, working_image_height );
}
function init_crop_selection()
{
var crop_size;
initial_crop_selection = [];
if( original_image_aspect_ratio > 1 )
{
crop_size = original_image_width;
}
else
{
crop_size = original_image_height;
}
if( original_image_aspect_ratio > 1 )
{
initial_crop_selection.push( 0 );
initial_crop_selection.push( 0 );
initial_crop_selection.push( crop_size );
initial_crop_selection.push( crop_size );
}
else
{
initial_crop_selection.push( ( original_image_width / 2 ) - ( crop_size / 2 ) );
initial_crop_selection.push( ( original_image_height / 2 ) - ( crop_size / 2 ) );
initial_crop_selection.push( ( original_image_width / 2 ) + ( crop_size / 2 ) );
initial_crop_selection.push( ( original_image_height / 2 ) + ( crop_size / 2 ) );
}
//console.debug( 'Initial selection: ', initial_crop_selection );
}
function render_content()
{
var content = jQuery( 'div#user_crop_content' );
var working_image = jQuery( '<img />', {
src: image_url
});
var workarea = jQuery( '<div />', {
id: 'user_crop_workarea',
style: {
'background-color': '#f2f2f2',
height: workarea_height + 'px',
width: workarea_width + 'px'
}
});
var previews = jQuery( '<div />', {
id: 'user_crop_preview',
style: {
'background-color': 'white',
height: preview_size + 'px',
'text-align': 'center',
'margin-top': gutter + 'px'
}
});
// Large square preview
var preview_lg_sq = jQuery( '<div />', {
class: 'preview_cropped_image'
}).css({
width: '128px',
height: '128px',
}).append( working_image.clone() );
// Small square preview
var preview_sm_sq = jQuery( '<div />', {
class: 'preview_cropped_image'
}).css({
width: '64px',
height: '64px',
}).append( working_image.clone() );
// Large circle preview
var preview_lg_c = jQuery( '<div />', {
class: 'preview_cropped_image circle'
}).css({
width: '128px',
height: '128px',
}).append( working_image.clone() );
// Small circle preview
var preview_sm_c = jQuery( '<div />', {
class: 'preview_cropped_image circle'
}).css({
width: '64px',
height: '64px',
}).append( working_image.clone() );
if( render_mode == 'portrait' )
{ // Portrait
workarea.css({
'background-color': '#f2f2f2',
height: workarea_height + 'px',
width: workarea_width + 'px'
});
previews.css({
'background-color': '#ffffff',
height: preview_size + 'px',
width: content_width + 'px',
'text-align': 'center',
'margin-top': gutter + 'px'
});
if( show_large_preview )
{
previews.append( preview_lg_sq );
}
if( show_small_preview )
{
previews.append( preview_sm_sq );
}
if( show_large_preview )
{
previews.append( preview_lg_c );
}
if( show_small_preview )
{
previews.append( preview_sm_c );
}
workarea.prepend( working_image );
content.append( workarea );
content.append( previews );
preview_images = jQuery( 'div#user_crop_preview div.preview_cropped_image:not(:last-child)' );
preview_images.css( 'margin-bottom', 0 );
preview_images.css( 'margin-right', preview_margin + 'px' );
}
else
{ // Landscape
workarea.css({
'background-color': '#f2f2f2',
float: 'left',
width: workarea_width + 'px',
height: workarea_height + 'px'
});
previews.css({
'background-color': '#ffffff',
float: 'left',
width: ( preview_orientation == 'portrait' ? preview_size : ( ( preview_size + preview_margin ) * 2 ) ) + 'px',
height: content_height + 'px',
'text-align': 'center',
'margin-left': gutter + 'px'
});
workarea.prepend( working_image );
var preview_wrapper = jQuery( '<div />' );
if( show_large_preview )
{
preview_wrapper.append( preview_lg_sq );
}
if( show_small_preview )
{
preview_wrapper.append( preview_sm_sq );
}
if( show_large_preview )
{
preview_wrapper.append( preview_lg_c );
}
if( show_small_preview )
{
preview_wrapper.append( preview_sm_c );
}
previews.append( preview_wrapper );
content.append( workarea );
content.append( previews );
content.append( jQuery( '<div />', { style: { clear: 'both' } } ) );
preview_images = jQuery( 'div#user_crop_preview div.preview_cropped_image:not(:last-child)' );
if( preview_orientation == 'portrait' )
{
preview_images.css( 'margin-bottom', preview_margin + 'px' );
preview_images.css( 'margin-right', 0 );
}
else
{
preview_images.css( 'margin-bottom', 0 );
preview_images.css( 'margin-right', preview_margin + 'px' );
}
}
// Adjust modal dimensions and content to minimize workarea margin
var wah_margin = workarea_height - ( original_image_height < working_image_height ? original_image_height : working_image_height );
var waw_margin = workarea_width - ( original_image_width < working_image_width ? original_image_width : working_image_width );
var modal_dialog = jQuery( 'div.modal-dialog' ).length ? jQuery( 'div.modal-dialog' ) : jQuery( '#overlay_wrap' );
var modal_body = jQuery( 'div.modal-body' ).length ? jQuery( 'div.modal-body' ) : jQuery( '#overlay_page' );
var content = jQuery( 'div#user_crop_content' );
//console.debug( 'Margin: ', wah_margin, waw_margin );
//console.debug( 'Workarea: ', workarea_height, workarea_width );
//console.debug( 'Working Image: ', working_image_height, working_image_width );
workarea.css( 'height', ( workarea_height - wah_margin ) + 'px' );
workarea.css( 'width', ( workarea_width - waw_margin ) + 'px' );
content.css( 'height', ( content_height - wah_margin ) + 'px' );
content.css( 'width', ( content_width - waw_margin + ( preview_orientation == 'landscape' ? preview_size + preview_margin + gutter : 0 ) ) + 'px' );
modal_body.css( 'height', ( parseInt( modal_body.css( 'height' ) ) - wah_margin ) + 'px' );
modal_body.css( 'min-height', ( parseInt( modal_body.css( 'min-height' ) ) - wah_margin ) + 'px' );
modal_body.css( 'width', ( parseInt( modal_body.css( 'width' ) ) - waw_margin + ( preview_orientation == 'landscape' ? preview_size + preview_margin + gutter : 0 ) ) + 'px' );
modal_dialog.css( 'height', ( parseInt( modal_dialog.css( 'height' ) ) - wah_margin ) + 'px' );
modal_dialog.css( 'width', ( parseInt( modal_dialog.css( 'width' ) ) - waw_margin + ( preview_orientation == 'landscape' ? preview_size + preview_margin + gutter : 0 ) ) + 'px' );
if( render_mode == 'portrait' )
{
previews.css( 'width', ( parseInt( previews.css( 'width' ) ) - waw_margin ) + 'px' );
}
else
{
previews.css( 'height', ( parseInt( previews.css( 'height' ) ) - wah_margin ) + 'px' );
}
}
function update_preview( coords )
{
var target_cropped_image_width = original_image_width;
var target_cropped_image_height = original_image_height;
var percent_width = Math.ceil( coords.w / target_cropped_image_width * 10000 ) / 100;
var percent_height = Math.ceil( coords.h / target_cropped_image_height * 10000 ) / 100;
var percent_top = Math.ceil( coords.x / target_cropped_image_width * 10000 ) / 100;
var percent_left = Math.ceil( coords.y / target_cropped_image_height * 10000 ) / 100;
jQuery( 'input[name=image_crop_data]' ).val( percent_top + ':' + percent_left + ':' + percent_width + ':' + percent_height );
var top = coords.y;
var left = coords.x;
if( coords.w > coords.h )
{ // Center a cropping area of horizontal image
left += ( coords.w / 2 ) - ( coords.h / 2 );
}
else
{
var top_shift = ( coords.h - coords.w ) * 0.15;
if( top + top_shift + coords.w < top + coords.h )
{ // top - 15%
top += top_shift;
}
}
jQuery( '.preview_cropped_image img' ).each( function()
{
var ratio = jQuery( this ).parent().width() / ( coords.w < coords.h ? coords.w : coords.h );
jQuery( this ).css({
width: Math.round( ratio * target_cropped_image_width ) + 'px',
height: Math.round( ratio * target_cropped_image_height ) + 'px',
marginLeft: '-' + Math.round( ratio * left ) + 'px',
marginTop: '-' + Math.round( ratio * top ) + 'px'
});
});
}
function selection_release()
{ // Reset to initial selection if selection is released
jcrop_api.setSelect( initial_crop_selection );
}
function init_jcrop_tool( image )
{
var min_avatar_size = <?php echo $min_avatar_size;?>;
options = {
boxWidth: working_image_width,
boxHeight: working_image_height,
aspectRatio: 1,
minSize: [ min_avatar_size, min_avatar_size ],
onChange: update_preview,
onSelect: update_preview,
onRelease: selection_release
};
image.Jcrop( options, function() {
jcrop_api = this;
jcrop_api.setSelect( initial_crop_selection );
image.css({ visibility: 'visible' });
});
}
// Initialize content, workarea and working image
init_layout();
init_workarea();
init_working_image();
init_crop_selection();
// Render everything
render_content();
// Initialize jcrop tool only after the image is fully loaded
jQuery( '#user_crop_workarea img' ).on( 'load', function()
{
init_jcrop_tool( jQuery( this ) );
});
</script>
<?php
}
?>