Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 68 additions & 11 deletions candidate/modules/v1/controllers/AccountController.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,11 @@ public function actionRemoveCivilPhotoBack() {
$model = Candidate::findOne(Yii::$app->user->getId());

if ($model->candidate_civil_photo_back) {
$model->deleteFile('civil-id', 'back');
$model->deleteFile('civil-id', 'back', true);
}

$model->candidate_civil_photo_back = null;
$model->candidate_civil_need_verification = true;

$model->scenario = 'updateCivilPhotoBack';

Expand All @@ -410,10 +411,11 @@ public function actionRemoveCivilPhotoFront() {
$model = Candidate::findOne(Yii::$app->user->getId());

if ($model->candidate_civil_photo_front) {
$model->deleteFile('civil-id', 'front');
$model->deleteFile('civil-id', 'front', true);
}

$model->candidate_civil_photo_front = null;
$model->candidate_civil_need_verification = true;

$model->scenario = 'updateCivilPhotoFront';

Expand Down Expand Up @@ -1301,7 +1303,12 @@ public function actionUpdateCivilPhotoBack() {
];
}

$model->updateCivilId('back');
if (!$model->updateCivilId('back')) {
return [
'operation' => 'error',
'message' => $model->getErrors()
];
}

//reset to remove old id's data
$model->candidate_civil_expiry_date = null;
Expand All @@ -1314,6 +1321,8 @@ public function actionUpdateCivilPhotoBack() {
];
}

$oldCivilIdCleanupComplete = $model->deletePendingCivilIdFiles('back');

return [
'operation' => 'success',
"candidate_civil_photo_back" => $model->candidate_civil_photo_back,
Expand All @@ -1322,6 +1331,7 @@ public function actionUpdateCivilPhotoBack() {
"candidate_civil_id" => $model->candidate_civil_id,
'civilExpired' => $model->candidate_civil_expiry_date && (strtotime($model->candidate_civil_expiry_date) <
strtotime(date('Y-m-d'))),
'civil_photo_cleanup_pending' => !$oldCivilIdCleanupComplete,

'message' => Yii::t('candidate', 'Civil Photo Back Uploaded Successfully')
];
Expand Down Expand Up @@ -1353,7 +1363,12 @@ public function actionUpdateCivilPhotoFront() {
];
}

$model->updateCivilId('front');
if (!$model->updateCivilId('front')) {
return [
'operation' => 'error',
'message' => $model->getErrors()
];
}

//reset to remove old id's data
$model->candidate_civil_expiry_date = null;
Expand All @@ -1366,6 +1381,8 @@ public function actionUpdateCivilPhotoFront() {
];
}

$oldCivilIdCleanupComplete = $model->deletePendingCivilIdFiles('front');

return [
'operation' => 'success',

Expand All @@ -1375,6 +1392,7 @@ public function actionUpdateCivilPhotoFront() {
"candidate_civil_id" => $model->candidate_civil_id,
'civilExpired' => $model->candidate_civil_expiry_date && (strtotime($model->candidate_civil_expiry_date) <
strtotime(date('Y-m-d'))),
'civil_photo_cleanup_pending' => !$oldCivilIdCleanupComplete,

'message' => Yii::t('candidate', 'Civil Photo Front Uploaded Successfully')
];
Expand Down Expand Up @@ -1430,24 +1448,63 @@ public function actionUpdateCivilIdExpiryDate() {
throw new \yii\web\HttpException(404, Yii::t('candidate', 'The requested Item could not be found.'));
}

$candidate_civil_id = Yii::$app->request->getBodyParam('civil_id');

$candidate_civil_id = trim((string) Yii::$app->request->getBodyParam('civil_id', ''));
$candidate_civil_expiry_date = Yii::$app->request->getBodyParam('civil_expiry_date');

$candidate->candidate_civil_id = $candidate_civil_id;
if ($candidate_civil_id === '') {
return [
'operation' => 'error',
'message' => Yii::t('candidate', 'Civil ID is required')
];
}

if($candidate_civil_expiry_date)
$candidate->candidate_civil_expiry_date = date('Y-m-d', strtotime($candidate_civil_expiry_date));
if (!preg_match('/^\d{12}$/', $candidate_civil_id)) {
return [
'operation' => 'error',
'message' => Yii::t('candidate', 'Civil ID must be 12 digit number')
];
}

if (!is_scalar($candidate_civil_expiry_date) || trim((string) $candidate_civil_expiry_date) === '') {
return [
'operation' => 'error',
'message' => Yii::t('candidate', 'Civil expiry date is required')
];
}

$expiryTimestamp = strtotime((string) $candidate_civil_expiry_date);
if ($expiryTimestamp === false) {
return [
'operation' => 'error',
'message' => Yii::t('candidate', 'Invalid civil expiry date')
];
}

$candidate->candidate_civil_id = $candidate_civil_id;
$candidate->candidate_civil_expiry_date = date('Y-m-d', $expiryTimestamp);

$candidate->candidate_civil_need_verification = true;

$candidate->scenario = "updateCivilExpiryDateAndCivilID";

if (!$candidate->save()) {
try {
if (!$candidate->save()) {
return [
"operation" => "error",
"message" => $candidate->getErrors()
];
}
} catch (\Throwable $e) {
Yii::error([
'message' => 'Failed to update civil ID and expiry date',
'route' => Yii::$app->requestedRoute,
'candidate_id' => $candidate->candidate_id,
'error' => $e->getMessage(),
], 'candidate');
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return [
"operation" => "error",
"message" => $candidate->errors
"message" => Yii::t('candidate', 'Unable to update Civil ID and expiry date right now')
];
}

Expand Down
45 changes: 45 additions & 0 deletions candidate/tests/functional/AccountCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,51 @@ public function tryUpdateCivilId(FunctionalTester $I)
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseContainsJson([ 'operation' => 'success','message' => 'Candidate Civil ID Info Updated Successfully']);
}

public function tryUpdateCivilIdExpiryDateRequiresCivilId(FunctionalTester $I)
{
$I->amGoingTo('reject empty civil id while updating civil id and expiry date');
$I->sendPOST('v1/account/update-civil-id-expiry-date', [
'civil_id' => '',
'civil_expiry_date' => date('Y-m-d', strtotime('+1 month')),
]);
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseContainsJson([
'operation' => 'error',
'message' => 'Civil ID is required',
]);
}

/**
* Verifies non-empty but invalid Civil IDs are rejected before saving.
*/
public function tryUpdateCivilIdExpiryDateRejectsShortCivilId(FunctionalTester $I)
{
$I->amGoingTo('reject short civil id while updating civil id and expiry date');
$I->sendPOST('v1/account/update-civil-id-expiry-date', [
'civil_id' => '70',
'civil_expiry_date' => date('Y-m-d', strtotime('+1 month')),
]);
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseContainsJson([
'operation' => 'error',
'message' => 'Civil ID must be 12 digits',
]);
}

public function tryUpdateCivilIdExpiryDateRejectsInvalidDate(FunctionalTester $I)
{
$I->amGoingTo('reject invalid civil expiry date while updating civil id and expiry date');
$I->sendPOST('v1/account/update-civil-id-expiry-date', [
'civil_id' => '123456789012',
'civil_expiry_date' => 'not-a-date',
]);
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseContainsJson([
'operation' => 'error',
'message' => 'Invalid civil expiry date',
]);
}

public function tryUpdateNationality(FunctionalTester $I)
{
Expand Down
59 changes: 49 additions & 10 deletions common/components/S3ResourceManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,25 +166,64 @@ public function delete($name)
}

/**
* Checks whether a file exists or not. This method only works for public resources, private resources will throw
* a 403 error exception.
* Returns the bucket/key pair for a stored object reference.
* Supports raw keys and S3 object URLs.
* @param string $filenameOrUrl
* @return array
*/
protected function resolveObjectLocation($filenameOrUrl)
{
$bucket = $this->bucket;
$key = ltrim((string) $filenameOrUrl, '/');

if (strpos((string) $filenameOrUrl, 'http') !== false) {
$parts = parse_url($filenameOrUrl);
$key = isset($parts['path']) ? ltrim($parts['path'], '/') : '';

if (!empty($parts['host']) && preg_match('/^([^.]+)\.s3[.-]/', $parts['host'], $matches)) {
$bucket = $matches[1];
} elseif (!empty($parts['host']) && preg_match('/^s3[.-]/', $parts['host']) && $key !== '') {
$segments = explode('/', $key, 2);
$bucket = $segments[0] ?: $bucket;
$key = $segments[1] ?? '';
}

$key = rawurldecode($key);
}

return [$bucket, $key];
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/**
* Returns S3 object metadata.
* @param string $filenameOrUrl
* @return \Aws\Result
*/
public function headObject($filenameOrUrl)
{
[$bucket, $key] = $this->resolveObjectLocation($filenameOrUrl);

return $this->getClient()->headObject([
'Bucket' => $bucket,
'Key' => $key,
]);
}

/**
* Checks whether a file exists or not using S3 metadata.
* @param string $filenameOrUrl the name or url of the file
* @return boolean
*/
public function fileExists($filenameOrUrl)
{
$isUrl = false;
if (strpos($filenameOrUrl, 'http') !== false) {
$isUrl = true;
}

$http = new \GuzzleHttp\Client(['base_uri' => $isUrl ? $filenameOrUrl : $this->getUrl($filenameOrUrl)]);
try {
$response = $http->request('HEAD');
$this->headObject($filenameOrUrl);
return true;
} catch (AwsException $e) {
return false;
} catch (\Exception $e) {
return false;
}
return $response->getStatusCode() == 200;
}

/**
Expand Down
Loading