فهرست منبع

Customer portal : Edit profile picture

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4231 a333f486-631f-4898-b8df-5754b55c2be0
glajarige 9 سال پیش
والد
کامیت
d1cde3ed0c

+ 66 - 0
datamodels/2.x/itop-portal-base/portal/src/controllers/userprofilebrickcontroller.class.inc.php

@@ -21,6 +21,7 @@ namespace Combodo\iTop\Portal\Controller;
 
 use \Exception;
 use \IssueLog;
+use \utils;
 use \MetaModel;
 use \UserRights;
 use \Silex\Application;
@@ -34,6 +35,7 @@ use \Combodo\iTop\Renderer\Bootstrap\BsFormRenderer;
 
 class UserProfileBrickController extends BrickController
 {
+	const ENUM_FORM_TYPE_PICTURE = 'picture';
 
 	public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId)
 	{
@@ -79,6 +81,10 @@ class UserProfileBrickController extends BrickController
 			{
 				$aData['form'] = $this->HandlePasswordForm($oRequest, $oApp);
 			}
+			elseif ($sFormType === static::ENUM_FORM_TYPE_PICTURE)
+			{
+				$aData['form'] = $this->HandlePictureForm($oRequest, $oApp, $sFormMode);
+			}
 			else
 			{
 				throw new Exception('Unknown form type.');
@@ -235,6 +241,66 @@ class UserProfileBrickController extends BrickController
 		return $aFormData;
 	}
 
+	public function HandlePictureForm(Request $oRequest, Application $oApp, $sFormMode)
+	{
+		$aFormData = array();
+		$oRequestParams = $oRequest->request;
+		$sPictureAttCode = 'picture';
+
+		// Handling form
+		$sOperation = $oRequestParams->get('operation');
+		// - No operation specified
+		if ($sOperation === null)
+		{
+			IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Operation parameter must be specified.');
+			$oApp->abort(500, 'Operation parameter must be specified.');
+		}
+		// - Submit
+		else if ($sOperation === 'submit')
+		{
+			$oRequestFiles = $oRequest->files;
+			$oPictureFile = $oRequestFiles->get($sPictureAttCode);
+			if ($oPictureFile === null)
+			{
+				IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameter picture must be defined.');
+				$oApp->abort(500, 'Parameter picture must be defined.');
+			}
+			
+			try
+			{
+				// Retrieving image as an ORMDocument
+				$oImage = utils::ReadPostedDocument($sPictureAttCode);
+				// Retrieving current contact
+				$oCurContact = UserRights::GetContactObject();
+				// Resizing image
+				$oAttDef = MetaModel::GetAttributeDef(get_class($oCurContact), $sPictureAttCode);
+				$aSize = utils::GetImageSize($oImage->GetData());
+				$oImage = utils::ResizeImageToFit($oImage, $aSize[0], $aSize[1], $oAttDef->Get('storage_max_width'), $oAttDef->Get('storage_max_height'));
+				// Setting it to the contact
+				$oCurContact->Set($sPictureAttCode, $oImage);
+				// Forcing allowed writing on the object if necessary.
+				$oCurContact->AllowWrite(true);
+				$oCurContact->DBUpdate();
+			}
+			catch (FileUploadException $e)
+			{
+				$aFormData['error'] = $e->GetMessage();
+			}
+
+			$aFormData['picture_url'] = $oImage->GetDownloadURL(get_class($oCurContact), $oCurContact->GetKey(), $sPictureAttCode);
+			$aFormData['validation'] = array(
+				'valid' => true,
+				'messages' => array()
+			);
+		}
+		else
+		{
+			// Else, submit from another form
+		}
+
+		return $aFormData;
+	}
+
 }
 
 ?>

+ 2 - 1
datamodels/2.x/itop-portal-base/portal/src/routers/userprofilebrickrouter.class.inc.php

@@ -29,7 +29,8 @@ class UserProfileRouter extends AbstractRouter
 			'bind' => 'p_user_profile_brick',
 			'values' => array(
 				'sBrickId' => null
-			))
+			)
+		)
 	);
 
 }

+ 67 - 7
datamodels/2.x/itop-portal-base/portal/src/views/bricks/user-profile/layout.html.twig

@@ -41,17 +41,37 @@
 				</div>
 			</div>
 			<div class="col-sm-6">
-				<div class="panel panel-default">
+				<div class="panel panel-default user_profile_picture">
 					<div class="panel-heading">
 						<h3 class="panel-title">Photo</h3>
 					</div>
 					<div class="panel-body" style="position: relative;">
-						<div class="text-center">
-							<img src="{{ sUserPhotoUrl }}" style="max-width: 175px;"/>
-							<!--<input type="file" id="xx" name="xx" />-->
-							<div style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; background-color: #000000; opacity: 0.5;"></div>
-							<div style="position: absolute; bottom: 0.5em; left: 0px; width: 100%; color: #FFFFFF; font-size: 1.5em; font-style: italic;">Picture edition not available in beta</div>
+						<div class="form_alerts">
+							<div class="alert alert-success" role="alert" style="display: none;"></div>
+							<div class="alert alert-warning" role="alert" style="display: none;"></div>
+							<div class="alert alert-error alert-danger" role="alert" style="display: none;"></div>
 						</div>
+						<form id="picture-form" method="POST" action="{{ app['url_generator'].generate('p_user_profile_brick') }}">
+							<input type="hidden" name="current_values[form_type]" value="{{ constant('\\Combodo\\iTop\\Portal\\Controller\\UserProfileBrickController::ENUM_FORM_TYPE_PICTURE') }}" />
+							<input type="hidden" name="operation" value="submit" />
+							<div class="text-center">
+								<span class="preview">
+									<img src="{{ sUserPhotoUrl }}"/>
+								</span>
+								<span class="actions">
+									<button type="button" class="btn btn-default btn_edit">
+										<span class="fa fa-pencil fa-fw"></span>
+										<input id="picture" type="file" name="picture" />
+									</button>
+									{#<button type="button" class="btn btn-default btn_undo" title="{{ 'UI:Button:ResetImage'|dict_s }}" disabled>
+										<span class="fa fa-undo fa-fw"></span>
+									</button>
+									<button type="button" class="btn btn-default btn_reset" title="{{ 'UI:Button:RemoveImage'|dict_s }}">
+										<span class="fa fa-trash-o fa-fw"></span>
+									</button>#}
+								</span>
+							</div>
+						</form>
 					</div>
 				</div>
 				<div class="panel panel-default">
@@ -136,6 +156,46 @@
 		});
 	{% endif %}
 	
+	// Picture form
+	// - JQuery upload widget
+	$('#picture-form #picture').fileupload({
+		dataType: 'json',
+		acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
+		disableImageResize: /Android(?!.*Chrome)|Opera/.test(window.navigator.userAgent)
+	})
+	.on('fileuploadsend', function(oEvent, oData){
+		$('.user_profile_picture .form_alerts .alert-error').hide()
+		$('#page_overlay .overlay_content .content_loader').clone().prependTo('.user_profile_picture .panel-body');
+	})
+	.on('fileuploadalways', function(oEvent, oData){
+		$('.user_profile_picture .content_loader').remove();
+	})
+	.on('fileuploaddone', function(oEvent, oData){
+		if( (oData._response.result.form !== undefined) && (oData._response.result.form.validation.valid === true) )
+		{
+			// Retrieving picture url
+			var sPictureUrl = oData._response.result.form.picture_url;
+			// Replacing form preview image
+			$('#picture-form .preview img').attr('src', sPictureUrl);
+			// Replacing menu image
+			$('#topbar .user_photo, #sidebar .user_photo').css('background-image', 'url("' + sPictureUrl + '")');
+		}
+	})
+	.on('fileuploadfail', function(oEvent, oData){
+		if( (oData._response.jqXHR.responseJSON !== undefined) && (oData._response.jqXHR.responseJSON.error_message !== undefined) )
+		{
+			$('.user_profile_picture .form_alerts .alert-error').show().text(oData._response.jqXHR.responseJSON.error_message);
+		}
+	});
+	// - Undo button
+	/*$('#user-profile-wrapper .actions .btn_undo').on('click', function(oEvent){
+		console.log('Picture undo trigger');
+	});*/
+	// - Reset button
+	$('#user-profile-wrapper .actions .btn_reset').on('click', function(oEvent){
+		console.log('Picture reset trigger');
+	});
+	
 	// Submit button
 	$('#user-profile-wrapper .form_buttons .form_btn_submit').off('click').on('click', function(oEvent){
 		oEvent.preventDefault();
@@ -168,6 +228,6 @@
 					return false;
 				}
 			});
-		{% endif %}
+		{% endif %}		
 	});
 {% endblock %}

+ 4 - 4
datamodels/2.x/itop-portal-base/portal/web/css/bootstrap-theme-combodo.scss

@@ -1617,8 +1617,8 @@ output {
 .form-control:focus {
     border-color: $brand-primary;
     outline: 0;
-    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
-    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6)
+    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(234, 125, 30, 0.6); /* Last rgb color is $brand-primary */
+    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(234, 125, 30, 0.6)
 }
 .form-control::-moz-placeholder {
     color: #dddddd;
@@ -2160,8 +2160,8 @@ fieldset[disabled] .btn-default:active,
 .btn-default.disabled.active,
 .btn-default[disabled].active,
 fieldset[disabled] .btn-default.active {
-    background-color: #474949;
-    border-color: #474949
+    background-color: #BBBBBB;
+    border-color: $gray-light;
 }
 .btn-default .badge {
     color: #474949;

+ 44 - 2
datamodels/2.x/itop-portal-base/portal/web/css/portal.scss

@@ -495,8 +495,50 @@ footer{
 /**********************/
 /* Brick user profile */
 /**********************/
-.home .userprofile-brick{
-	background-color: #E8E7E7;
+#user-profile-wrapper .user_profile_picture .content_loader{
+	position: absolute;
+	z-index: 1;
+    top: 0;
+    left: 0;
+    padding-top: 4em;
+    width: 100%;
+    height: 100%;
+	text-align: center;
+	color: white;
+    background-color: black;
+    opacity: 0.5;
+}
+#user-profile-wrapper .user_profile_picture .preview{
+	display: inline-block;
+	position: relative;
+	max-width: 175px;
+	max-height: 175px;
+	overflow: hidden;
+}
+#user-profile-wrapper .user_profile_picture .actions{
+	display: inline-block;
+	vertical-align: top; /*middle;*/
+	margin-left: 5px;
+}
+#user-profile-wrapper .user_profile_picture .actions .btn{
+	display: block;
+	position: relative;
+	margin-bottom: 10px;
+}
+#user-profile-wrapper .user_profile_picture .actions .btn:last-child{
+	margin-bottom: 0px;
+}
+#user-profile-wrapper .user_profile_picture .actions .btn.btn_edit{
+	overflow: hidden;
+}
+#user-profile-wrapper .user_profile_picture .actions .btn.btn_edit input{
+	position: absolute;
+	top: 0px;
+	left: 0px;
+	width: 100%;
+	height: 100%;
+	opacity: 0;
+	cursor: pointer;
 }
 
 /****************/