-
-
Notifications
You must be signed in to change notification settings - Fork 76
Develop #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Develop #255
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
184 changes: 184 additions & 0 deletions
184
Web/Resgrid.Web.Services/Controllers/v4/AvatarsController.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| using Microsoft.AspNetCore.Http; | ||
| using Microsoft.AspNetCore.Mvc; | ||
| using Resgrid.Framework; | ||
| using Resgrid.Model; | ||
| using Resgrid.Model.Services; | ||
| using System; | ||
| using System.IO; | ||
| using System.Net.Mime; | ||
| using System.Threading.Tasks; | ||
|
|
||
| using Resgrid.Web.Services.Models; | ||
| using Resgrid.Web.ServicesCore.Helpers; | ||
| using SixLabors.ImageSharp; | ||
| using SixLabors.ImageSharp.Processing; | ||
|
|
||
|
|
||
| namespace Resgrid.Web.Services.Controllers.v4 | ||
| { | ||
| /// <summary> | ||
| /// Used to interact with the user avatars (profile pictures) in the Resgrid system. The authentication header isn't required to access this method. | ||
| /// </summary> | ||
| [Route("api/v{VersionId:apiVersion}/[controller]")] | ||
| [ApiVersion("4.0")] | ||
| [ApiExplorerSettings(GroupName = "v4")] | ||
| //[EnableCors("_resgridWebsiteAllowSpecificOrigins")] | ||
| public class AvatarsController : ControllerBase | ||
| { | ||
| private readonly IImageService _imageService; | ||
| private static byte[] _defaultProfileImage; | ||
|
|
||
| public AvatarsController(IImageService imageService) | ||
| { | ||
| _imageService = imageService; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Get a users avatar from the Resgrid system based on their ID | ||
| /// </summary> | ||
| /// <param name="id">ID of the user</param> | ||
| /// <returns></returns> | ||
| [HttpGet("Get")] | ||
| [Produces(MediaTypeNames.Image.Jpeg)] | ||
| [ProducesResponseType(StatusCodes.Status200OK)] | ||
| [ProducesResponseType(StatusCodes.Status404NotFound)] | ||
| public async Task<ActionResult> Get(string id, int? type) | ||
| { | ||
| byte[] data = null; | ||
| if (type == null) | ||
| data = await _imageService.GetImageAsync(ImageTypes.Avatar, id); | ||
| else | ||
| data = await _imageService.GetImageAsync((ImageTypes)type.Value, id); | ||
|
|
||
| if (data == null || data.Length <= 0) | ||
| return File(GetDefaultProfileImage(), "image/png"); | ||
|
|
||
| return File(data, "image/jpeg"); | ||
| } | ||
|
|
||
| [HttpPost("Upload")] | ||
| [ProducesResponseType(StatusCodes.Status200OK)] | ||
| [ProducesResponseType(StatusCodes.Status201Created)] | ||
| [ProducesResponseType(StatusCodes.Status400BadRequest)] | ||
| public async Task<ActionResult> Upload([FromQuery] string id, int? type) | ||
| { | ||
| var img = HttpContext.Request.Form.Files.Count > 0 ? | ||
| HttpContext.Request.Form.Files[0] : null; | ||
|
|
||
| // check for a valid mediatype | ||
| if (!img.ContentType.StartsWith("image/")) | ||
| return BadRequest(); | ||
|
|
||
| // load the image from the upload and generate a new filename | ||
| //var image = Image.FromStream(img.OpenReadStream()); | ||
| var extension = Path.GetExtension(img.FileName); | ||
| byte[] imgArray; | ||
| int width = 0; | ||
| int height = 0; | ||
|
|
||
| using (Image image = Image.Load(img.OpenReadStream())) | ||
| { | ||
| //image.Mutate(x => x | ||
| // .Resize(image.Width / 2, image.Height / 2) | ||
| // .Grayscale()); | ||
|
|
||
| width = image.Width; | ||
| height = image.Height; | ||
|
|
||
| MemoryStream ms = new MemoryStream(); | ||
| await image.SaveAsPngAsync(ms); | ||
| imgArray = ms.ToArray(); | ||
|
|
||
| //image.Save()"output/fb.png"); // Automatic encoder selected based on extension. | ||
| } | ||
|
|
||
| //ImageConverter converter = new ImageConverter(); | ||
| //byte[] imgArray = (byte[])converter.ConvertTo(image, typeof(byte[])); | ||
|
|
||
| if (type == null) | ||
| await _imageService.SaveImageAsync(ImageTypes.Avatar, id, imgArray); | ||
| else | ||
| await _imageService.SaveImageAsync((ImageTypes)type.Value, id, imgArray); | ||
|
|
||
| var baseUrl = Config.SystemBehaviorConfig.ResgridApiBaseUrl; | ||
|
|
||
| string url; | ||
|
|
||
| if (type == null) | ||
| url = baseUrl + "/api/v4/Avatars/Get?id=" + id; | ||
| else | ||
| url = baseUrl + "/api/v4/Avatars/Get?id=" + id + "&type=" + type.Value; | ||
|
|
||
| var obj = new | ||
| { | ||
| status = CroppicStatuses.Success, | ||
| url = url, | ||
| width = width, | ||
| height = height | ||
| }; | ||
|
|
||
| return CreatedAtAction(nameof(Upload), new { id = obj.url }, obj); | ||
| } | ||
|
|
||
| [HttpPut("Crop")] | ||
| [ProducesResponseType(StatusCodes.Status200OK)] | ||
| [ProducesResponseType(StatusCodes.Status201Created)] | ||
| [ProducesResponseType(StatusCodes.Status400BadRequest)] | ||
| [ProducesResponseType(StatusCodes.Status500InternalServerError)] | ||
| public async Task<ActionResult> Crop([FromBody] CropRequest model) | ||
| { | ||
| // extract original image ID and generate a new filename for the cropped result | ||
| var originalUri = new Uri(model.imgUrl); | ||
| var originalId = originalUri.Query.Replace("?id=", ""); | ||
|
|
||
| try | ||
| { | ||
| byte[] imgArray; | ||
|
|
||
| using (var ms = new MemoryStream(await _imageService.GetImageAsync(ImageTypes.Avatar, originalId))) | ||
| using (var image = Image.Load(ms)) | ||
| { | ||
| // load the original picture and resample it to the scaled values | ||
| var bitmap = ImageUtils.Resize(image, (int)model.imgW, (int)model.imgH); | ||
|
|
||
| var croppedBitmap = ImageUtils.Crop(bitmap, model.imgX1, model.imgY1, model.cropW, model.cropH); | ||
|
|
||
| using (var ms2 = new MemoryStream()) | ||
| { | ||
| await croppedBitmap.SaveAsPngAsync(ms2); | ||
| imgArray = ms2.ToArray(); | ||
| } | ||
| } | ||
|
|
||
| await _imageService.SaveImageAsync(ImageTypes.Avatar, originalId, imgArray); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Logging.LogException(ex, $"Error cropping avatar image for ID: {originalId}"); | ||
| return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while cropping the image"); | ||
| } | ||
|
|
||
| var obj = new | ||
| { | ||
| status = CroppicStatuses.Success, | ||
| url = originalId | ||
| }; | ||
|
|
||
| return CreatedAtAction(nameof(Crop), new { id = obj.url }, obj); | ||
| } | ||
|
|
||
| private byte[] GetDefaultProfileImage() | ||
| { | ||
| if (_defaultProfileImage == null) | ||
| _defaultProfileImage = EmbeddedResources.GetApiRequestFile(typeof(AvatarsController), "Resgrid.Web.Services.Properties.Resources.defaultProfile.png"); | ||
|
|
||
| return _defaultProfileImage; | ||
| } | ||
| } | ||
|
|
||
| internal static class CroppicStatuses | ||
| { | ||
| public const string Success = "success"; | ||
| public const string Error = "error"; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistency: TMobile added to DirectSendCarriers but CarriersMap still uses email gateway.
Adding
MobileCarriers.TMobiletoDirectSendCarriersimplies it should use direct SMS API, but line 195 inCarriersMapstill maps TMobile to the email gateway"{0}@tmomail.net". This is inconsistent with other carriers that use direct send (e.g., TracFone at line 197, BellMobility at line 203, TelusMobility at line 204 all map to"Direct").This mismatch could cause TMobile messages to be routed incorrectly depending on which mapping the SMS sending logic consults first.
Apply this diff to align the CarriersMap with the DirectSendCarriers configuration:
Alternatively, if TMobile should continue using the email gateway, remove it from DirectSendCarriers instead.
🤖 Prompt for AI Agents