public async Task ActionComment(int userId, int commentId) { var comment = DataModel.UserComments.FirstOrDefault(c => c.Id == commentId); comment.IsActionedByEditor = true; DataModel.SaveChanges(); var user = new UserManager().GetUser(userId); AuditLogManager.Log(user, AuditEventType.UpdatedItem, "{EntityType:\"Comment\",EntityID:" + commentId + ",ChargePointID:" + comment.ChargePointId + "}", "User marked comment as actioned"); //refresh cached POI data await CacheManager.RefreshCachedPOI(comment.ChargePointId); }
public void DeleteComment(int userId, int commentId) { var comment = DataModel.UserComments.FirstOrDefault(c => c.ID == commentId); if (comment != null) { var cpID = comment.ChargePointID; DataModel.UserComments.Remove(comment); DataModel.ChargePoints.Find(cpID).DateLastStatusUpdate = DateTime.UtcNow; DataModel.SaveChanges(); var user = new UserManager().GetUser(userId); AuditLogManager.Log(user, AuditEventType.DeletedItem, "{EntityType:\"Comment\",EntityID:" + commentId + ",ChargePointID:" + cpID + "}", "User deleted comment"); CacheManager.RefreshCachedPOIList(); } }
public void DeleteMediaItem(int userId, int mediaItemId) { var dataModel = new OCMEntities(); var item = dataModel.MediaItems.FirstOrDefault(c => c.ID == mediaItemId); if (item != null) { var cpID = item.ChargePointID; dataModel.MediaItems.Remove(item); dataModel.ChargePoints.Find(cpID).DateLastStatusUpdate = DateTime.UtcNow; dataModel.SaveChanges(); //TODO: delete from underlying storage var user = new UserManager().GetUser(userId); AuditLogManager.Log(user, AuditEventType.DeletedItem, "{EntityType:\"Comment\", EntityID:" + mediaItemId + ",ChargePointID:" + cpID + "}", "User deleted media item"); } }
public string[] UploadPOIImageToStorage(string tempFolder, string sourceImageFile, Model.ChargePoint poi) { string extension = sourceImageFile.Substring(sourceImageFile.LastIndexOf('.'), sourceImageFile.Length - sourceImageFile.LastIndexOf('.')).ToLower(); if (extension != ".jpg" && extension != ".jpeg" && extension != ".png" && extension != ".gif") { return(null); } var storage = new StorageManager(); try { //TODO: allocate sequences properly string destFolderPrefix = poi.AddressInfo.Country.ISOCode + "/" + "OCM" + poi.ID + "/"; string dateStamp = String.Format("{0:yyyyMMddHHmmssff}", DateTime.UtcNow); string largeFileName = "OCM-" + poi.ID + ".orig." + dateStamp + extension; string thumbFileName = "OCM-" + poi.ID + ".thmb." + dateStamp + extension; string mediumFileName = "OCM-" + poi.ID + ".medi." + dateStamp + extension; var metadataTags = new List <KeyValuePair <string, string> >(); metadataTags.Add(new KeyValuePair <string, string>("OCM", poi.ID.ToString())); //metadataTags.Add(new KeyValuePair<string, string>("Title", poi.AddressInfo.Title)); metadataTags.Add(new KeyValuePair <string, string>("Latitude", poi.AddressInfo.Latitude.ToString())); metadataTags.Add(new KeyValuePair <string, string>("Longitude", poi.AddressInfo.Longitude.ToString())); //TODO: generate thumbnail var urls = new string[3]; //attempt thumbnails try { //generate thumbnail max 100 wide GenerateImageThumbnails(sourceImageFile, tempFolder + thumbFileName, 100); //generate medium max 400 wide GenerateImageThumbnails(sourceImageFile, tempFolder + mediumFileName, 400); //resize original max 2048 GenerateImageThumbnails(sourceImageFile, tempFolder + largeFileName, 2048); } catch (Exception) { AuditLogManager.Log(null, AuditEventType.SystemErrorAPI, "Failed to generate image upload thumbnails : OCM-" + poi.ID, ""); return(null); } //attempt upload bool success = false; int attemptCount = 0; while (success == false && attemptCount < 5) { attemptCount++; try { if (urls[0] == null) { urls[0] = storage.UploadImage(sourceImageFile, destFolderPrefix + largeFileName, metadataTags); } if (urls[1] == null) { urls[1] = storage.UploadImage(tempFolder + thumbFileName, destFolderPrefix + thumbFileName, metadataTags); } if (urls[2] == null) { urls[2] = storage.UploadImage(tempFolder + mediumFileName, destFolderPrefix + mediumFileName, metadataTags); } if (urls[0] != null && urls[1] != null && urls[2] != null) { success = true; } } catch (Exception exp) { //failed to store blobs AuditLogManager.Log(null, AuditEventType.SystemErrorAPI, "Failed to upload images to azure (attempt " + attemptCount + "): OCM-" + poi.ID, exp.ToString()); //return null; } attemptCount++; Thread.Sleep(5000); //wait a bit then try again } Thread.Sleep(5000); //attempt to delete temp files try { System.IO.File.Delete(sourceImageFile); System.IO.File.Delete(tempFolder + thumbFileName); System.IO.File.Delete(tempFolder + mediumFileName); System.IO.File.Delete(tempFolder + largeFileName); } catch (Exception) { ; } return(urls); } catch (Exception) { //failed to upload AuditLogManager.Log(null, AuditEventType.SystemErrorAPI, "Final attempt to upload images to azure failed: OCM-" + poi.ID, ""); return(null); } }
public bool GrantPermission(User user, StandardPermissionAttributes permissionAttribute, string attributeValue, bool removeOnly, User administrator) { //to apply permissions we add or remove from the permissions list attached to the user details, we also maintain a string in the legacy semicolon seperated format for apps/code which still requires the older format. var userDetails = dataModel.Users.FirstOrDefault(u => u.Id == user.ID); if (userDetails != null) { UserPermissionsContainer userPermissions = new UserPermissionsContainer(); if (!String.IsNullOrEmpty(user.Permissions)) { userPermissions = JsonConvert.DeserializeObject <UserPermissionsContainer>(user.Permissions); } //apply permission to legacypermission tag of user details string attributeTag = "[" + permissionAttribute.ToString() + "=" + attributeValue + "];"; if (userPermissions.LegacyPermissions == null) { userPermissions.LegacyPermissions = ""; } if (userPermissions.Permissions == null) { userPermissions.Permissions = new List <UserPermission>(); } if (!removeOnly) { //add permission //append permission attribute for user //legacy format is [AttributeName1=Value];[AttributeName2=Value]; -legacy format is maintained as LegacyPermissions field in JSON format, for older apps (mainly older versions of OCM app) if (!userPermissions.LegacyPermissions.Contains(attributeTag)) { if (!userPermissions.LegacyPermissions.EndsWith(";") && userPermissions.LegacyPermissions != "") { userPermissions.LegacyPermissions += ";"; } userPermissions.LegacyPermissions += attributeTag; //add permission to main permission list if (permissionAttribute == StandardPermissionAttributes.CountryLevel_Editor) { var permission = new UserPermission(); if (attributeValue != "All") { permission.CountryID = int.Parse(attributeValue); } permission.Level = PermissionLevel.Editor; userPermissions.Permissions.Add(permission); } //TODO: administrator permissions AuditLogManager.Log(administrator, AuditEventType.PermissionGranted, "User: "******"; Permission:" + permissionAttribute.ToString(), null); } } else { //remove permission userPermissions.LegacyPermissions = userPermissions.LegacyPermissions.Replace(attributeTag, ""); if (permissionAttribute == StandardPermissionAttributes.CountryLevel_Editor) { if (attributeValue != "All") { int countryID = int.Parse(attributeValue); userPermissions.Permissions.RemoveAll(p => p.Level == PermissionLevel.Editor && p.CountryID == countryID); } else { userPermissions.Permissions.RemoveAll(p => p.Level == PermissionLevel.Editor); } } AuditLogManager.Log(administrator, AuditEventType.PermissionRemoved, "User: "******"; Permission:" + permissionAttribute.ToString(), null); } //remove requested permission attribute if it exists if (userDetails.PermissionsRequested != null) { userDetails.PermissionsRequested = userDetails.PermissionsRequested.Replace(attributeTag, ""); } userDetails.Permissions = JsonConvert.SerializeObject(userPermissions, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); dataModel.SaveChanges(); return(true); } else { return(false); } }
/// <summary> /// Consumers should prepare a new/updated ChargePoint with as much info populated as possible /// </summary> /// <param name="submission">ChargePoint info for submission, if ID and UUID set will be treated as an update</param> /// <returns>false on error or not enough data supplied</returns> public async Task <ValidationResult> PerformPOISubmission(Model.ChargePoint updatedPOI, Model.User user, bool performCacheRefresh = true, bool disablePOISuperseding = false) { try { var poiManager = new POIManager(); bool enableEditQueueLogging = bool.Parse(ConfigurationManager.AppSettings["EnableEditQueue"]); bool isUpdate = false; bool userCanEditWithoutApproval = false; bool isSystemUser = false; int? supersedesID = null;//POI from another data provider which has been superseded by an edit //if user signed in, check if they have required permission to perform an edit/approve (if required) if (user != null) { if (user.ID == (int)StandardUsers.System) { isSystemUser = true; } //if user is system user, edits/updates are not recorded in edit queue if (isSystemUser) { enableEditQueueLogging = false; } userCanEditWithoutApproval = POIManager.CanUserEditPOI(updatedPOI, user); } var dataModel = new Core.Data.OCMEntities(); //if poi is an update, validate if update can be performed if (updatedPOI.ID > 0 && !String.IsNullOrEmpty(updatedPOI.UUID)) { if (dataModel.ChargePoints.Any(c => c.Id == updatedPOI.ID && c.Uuid == updatedPOI.UUID)) { //update is valid poi, check if user has permission to perform an update isUpdate = true; if (userCanEditWithoutApproval) { AllowUpdates = true; } if (!AllowUpdates && !enableEditQueueLogging) { //valid update requested but updates not allowed return(new ValidationResult { IsValid = false, Message = "Updates are disabled" }); } } else { //update does not correctly identify an existing poi return(new ValidationResult { IsValid = false, Message = "Update does not correctly match an existing POI" }); } } //validate if minimal required data is present if (updatedPOI.AddressInfo.Title == null || (updatedPOI.AddressInfo.Country == null && updatedPOI.AddressInfo.CountryID == null)) { return(new ValidationResult { IsValid = false, Message = "Update failed basic validation" }); } //convert to DB version of POI and back so that properties are fully populated using (var refDataManager = new ReferenceDataManager()) { var refData = await refDataManager.GetCoreReferenceDataAsync(); updatedPOI = PopulateFullPOI(updatedPOI, refData); } Model.ChargePoint oldPOI = null; if (updatedPOI.ID > 0) { //get json snapshot of current cp data to store as 'previous' oldPOI = await poiManager.Get(updatedPOI.ID); } //if user cannot edit directly, add to edit queue for approval var editQueueItem = new Core.Data.EditQueueItem { DateSubmitted = DateTime.UtcNow }; if (enableEditQueueLogging) { editQueueItem.EntityId = updatedPOI.ID; editQueueItem.EntityType = dataModel.EntityTypes.FirstOrDefault(t => t.Id == 1); //charging point location entity type id //serialize cp as json //null extra data we don't want to serialize/compare updatedPOI.UserComments = null; updatedPOI.MediaItems = null; string editData = PerformSerialisationToString(updatedPOI, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); editQueueItem.EditData = editData; if (updatedPOI.ID > 0) { //check if poi will change with this edit, if not we discard it completely if (!poiManager.HasDifferences(oldPOI, updatedPOI)) { System.Diagnostics.Debug.WriteLine("POI Update has no changes, discarding change."); return(new ValidationResult { IsValid = true, ItemId = updatedPOI.ID, Message = "No POI changes detected" }); } else { editQueueItem.PreviousData = PerformSerialisationToString(oldPOI, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); } } if (user != null) { editQueueItem.User = dataModel.Users.FirstOrDefault(u => u.Id == user.ID); } var processedByUser = editQueueItem.User ?? dataModel.Users.FirstOrDefault(u => u.Id == (int)StandardUsers.System); editQueueItem.IsProcessed = false; dataModel.EditQueueItems.Add(editQueueItem); //TODO: send notification of new item for approval //save edit queue item dataModel.SaveChanges(); //if previous edit queue item exists by same user for same POI, mark as processed var previousEdits = dataModel.EditQueueItems.Where(e => e.UserId == editQueueItem.UserId && e.EntityId == editQueueItem.EntityId && e.EntityTypeId == editQueueItem.EntityTypeId && e.Id != editQueueItem.Id && e.IsProcessed != true); foreach (var previousEdit in previousEdits) { previousEdit.IsProcessed = true; previousEdit.ProcessedByUser = processedByUser; previousEdit.DateProcessed = DateTime.UtcNow; } //save updated edit queue items dataModel.SaveChanges(); } //prepare and save changes POI changes/addition if (isUpdate && !AllowUpdates) { //user has submitted an edit but is not an approved editor //SendEditSubmissionNotification(updatedPOI, user); //user is not an editor, item is now pending in edit queue for approval. return(new ValidationResult { IsValid = true, ItemId = updatedPOI.ID, Message = "Update submitted for review" }); } if (isUpdate && updatedPOI.SubmissionStatusTypeID >= 1000) { //update is a delisting, skip superseding poi System.Diagnostics.Debug.WriteLine("skipping superseding of imported POI due to delisting"); } else { //if poi being updated exists from an imported source we supersede the old POI with the new version, unless we're doing a fresh import from same data provider if (!disablePOISuperseding) { //if update by non-system user will change an imported/externally provided data, supersede old POI with new one (retain ID against new POI) if (isUpdate && !isSystemUser && oldPOI.DataProviderID != (int)StandardDataProviders.OpenChargeMapContrib) { //move old poi to new id, set status of new item to superseded supersedesID = poiManager.SupersedePOI(dataModel, oldPOI, updatedPOI); } } } //user is an editor, go ahead and store the addition/update //set/update cp properties var cpData = poiManager.PopulateChargePoint_SimpleToData(updatedPOI, dataModel); //if item has no submission status and user permitted to edit, set to published if (userCanEditWithoutApproval && cpData.SubmissionStatusTypeId == null) { cpData.SubmissionStatusTypeId = (int)StandardSubmissionStatusTypes.Submitted_Published; //hack due to conflicting state change for SubmissionStatusType } else { //no submission status, set to 'under review' if (cpData.SubmissionStatusTypeId == null) { cpData.SubmissionStatusTypeId = (int)StandardSubmissionStatusTypes.Submitted_UnderReview; } } cpData.DateLastStatusUpdate = DateTime.UtcNow; if (!isUpdate) { //new data objects need added to data model before save if (cpData.AddressInfo != null) { dataModel.AddressInfos.Add(cpData.AddressInfo); } dataModel.ChargePoints.Add(cpData); } //finally - save poi update dataModel.SaveChanges(); //get id of update/new poi int newPoiID = cpData.Id; //this is an authorised update, reflect change in edit queue item if (enableEditQueueLogging && user != null && user.ID > 0) { var editUser = dataModel.Users.FirstOrDefault(u => u.Id == user.ID); editQueueItem.User = editUser; if (newPoiID > 0) { editQueueItem.EntityId = newPoiID; } //if user is authorised to edit, process item automatically without review if (userCanEditWithoutApproval) { editQueueItem.ProcessedByUser = editUser; editQueueItem.DateProcessed = DateTime.UtcNow; editQueueItem.IsProcessed = true; } //save edit queue item changes dataModel.SaveChanges(); } else { //anonymous submission, update edit queue item if (enableEditQueueLogging && user == null) { if (newPoiID > 0) { editQueueItem.EntityId = newPoiID; } dataModel.SaveChanges(); } } System.Diagnostics.Debug.WriteLine("Added/Updated CP:" + cpData.Id); //if user is not anonymous, log their submission and update their reputation points if (user != null) { AuditLogManager.Log(user, isUpdate ? AuditEventType.UpdatedItem : AuditEventType.CreatedItem, "Modified OCM-" + cpData.Id, null); //add reputation points new UserManager().AddReputationPoints(user, 1); } //preserve new POI Id for caller updatedPOI.ID = cpData.Id; if (performCacheRefresh) { if (supersedesID != null) { await CacheManager.RefreshCachedPOI((int)supersedesID); } await CacheManager.RefreshCachedPOI(updatedPOI.ID); } return(new ValidationResult { IsValid = true, ItemId = updatedPOI.ID, Message = "Update submitted." }); } catch (Exception exp) { System.Diagnostics.Debug.WriteLine(exp.ToString()); AuditLogManager.ReportWebException(true, null, AuditEventType.SystemErrorWeb, "POI Submission Failed", exp); //error performing submission return(new ValidationResult { IsValid = false, Message = "Submission Failed with an Exception: " + exp.Message }); } }
/// <summary> /// Submit a new comment against a given charge equipment id /// </summary> /// <param name="comment"></param> /// <returns>ID of new comment, -1 for invalid cp, -2 for general error saving comment</returns> public async Task <int> PerformSubmission(Common.Model.UserComment comment, Model.User user) { //TODO: move all to UserCommentManager //populate data model comment from simple comment object var dataModel = new Core.Data.OCMEntities(); int cpID = comment.ChargePointID; var dataComment = new Core.Data.UserComment(); var dataChargePoint = dataModel.ChargePoints.FirstOrDefault(c => c.Id == cpID); if (dataChargePoint == null) { return(-1); //invalid charge point specified } dataComment.ChargePointId = dataChargePoint.Id; dataComment.Comment = comment.Comment; int commentTypeID = comment.CommentTypeID ?? 10; //default to General Comment // some clients may post a CommentType object instead of just an ID if (comment.CommentType != null) { commentTypeID = comment.CommentType.ID; } dataComment.UserCommentTypeId = commentTypeID; int?checkinStatusType = comment.CheckinStatusTypeID; dataComment.CheckinStatusTypeId = (byte?)comment.CheckinStatusTypeID; // some clients may post a CheckinStatusType object instead of just an ID if (dataComment.CheckinStatusTypeId == null && comment.CheckinStatusType != null) { dataComment.CheckinStatusTypeId = (byte?)comment.CheckinStatusType.ID; } dataComment.UserName = comment.UserName; dataComment.Rating = comment.Rating; dataComment.RelatedUrl = comment.RelatedURL; dataComment.DateCreated = DateTime.UtcNow; if (user != null && user.ID > 0) { var ocmUser = dataModel.Users.FirstOrDefault(u => u.Id == user.ID); if (ocmUser != null) { dataComment.UserId = ocmUser.Id; dataComment.UserName = ocmUser.Username; } } try { dataChargePoint.DateLastStatusUpdate = DateTime.UtcNow; dataModel.UserComments.Add(dataComment); dataModel.SaveChanges(); if (user != null) { AuditLogManager.Log(user, AuditEventType.CreatedItem, "Added Comment " + dataComment.Id + " to OCM-" + cpID, null); //add reputation points new UserManager().AddReputationPoints(user, 1); } //SendPOICommentSubmissionNotifications(comment, user, dataComment); //TODO: only refresh cache for specific POI await CacheManager.RefreshCachedPOI(dataComment.ChargePoint.Id); return(dataComment.Id); } catch (Exception exp) { return(-2); //error saving } }
public static void ReportWebException(HttpServerUtility Server, AuditEventType eventType) { bool ignoreException = false; string body = "An error has occurred while a user was browsing OCM:<br><br>"; object exceptionObject = null; if (Server.GetLastError() != null) { Exception exp = Server.GetLastError(); exceptionObject = exp; if (exp.InnerException != null) { exceptionObject = exp.InnerException; } body += ((Exception)exceptionObject).ToString(); HttpContext con = HttpContext.Current; if (con.Request.Url != null) { body += "<br><br>Request Url:" + con.Request.Url.ToString(); //special case to avoid reporting /trackback url exceptions if (con.Request.Url.ToString().EndsWith("/trackback/")) { ignoreException = true; } } if (con.Request.UserAgent != null) { body += "<br>User Agent: " + con.Request.UserAgent; } body += "<br><br>" + DateTime.UtcNow.ToString(); } if (exceptionObject is System.Web.HttpRequestValidationException || exceptionObject is System.Web.UI.ViewStateException) { ignoreException = true; } if (!ignoreException) { if (ConfigurationManager.AppSettings["EnableErrorNotifications"] == "true") { NotificationManager notification = new NotificationManager(); var msgParams = new Hashtable() { { "Description", "System Error" }, { "Name", "OCM Website" }, { "Email", "*****@*****.**" }, { "Comment", body } }; notification.PrepareNotification(NotificationType.ContactUsMessage, msgParams); notification.SendNotification(NotificationType.ContactUsMessage); } AuditLogManager.Log(null, eventType, body, null); } }
public static void ReportWebException(bool enableNotifications, string contextUrl, AuditEventType eventType, string msg = null, Exception exp = null) { bool ignoreException = false; string body = "An error has occurred while a user was browsing OCM:<br><br>"; if (msg != null) { body = msg; } if (exp != null) { object exceptionObject = exp; if (exp.InnerException != null) { exceptionObject = exp.InnerException; } body += ((Exception)exceptionObject).ToString(); if (contextUrl != null) { body += "<br><br>Request Url:" + contextUrl.ToString(); //special case to avoid reporting /trackback url exceptions if (contextUrl.ToString().EndsWith("/trackback/")) { ignoreException = true; } } /*if (con.Request.UserAgent != null) * { * body += "<br>User Agent: " + con.Request.UserAgent; * }*/ } body += "<br><br>" + DateTime.UtcNow.ToString(); //if (exp is System.Web.HttpRequestValidationException || exceptionObject is System.Web.UI.ViewStateException) ignoreException = true; if (!ignoreException) { if (enableNotifications) { NotificationManager notification = new NotificationManager(); var msgParams = new Hashtable() { { "Description", "System Error" }, { "Name", "OCM Website" }, { "Email", "*****@*****.**" }, { "Comment", body } }; notification.PrepareNotification(NotificationType.ContactUsMessage, msgParams); notification.SendNotification(NotificationType.ContactUsMessage); } AuditLogManager.Log(null, eventType, body, null); } }
/// <summary> /// Submit a new comment against a given charge equipment id /// </summary> /// <param name="comment"></param> /// <returns>ID of new comment, -1 for invalid cp, -2 for general error saving comment</returns> public int PerformSubmission(Common.Model.UserComment comment, Model.User user) { //TODO: move all to UserCommentManager //populate data model comment from simple comment object var dataModel = new Core.Data.OCMEntities(); int cpID = comment.ChargePointID; var dataComment = new Core.Data.UserComment(); dataComment.ChargePoint = dataModel.ChargePoints.FirstOrDefault(c => c.ID == cpID); if (dataComment.ChargePoint == null) { return(-1); //invalid charge point specified } dataComment.Comment = comment.Comment; int commentTypeID = 10; //default to General Comment if (comment.CommentType != null) { commentTypeID = comment.CommentType.ID; } dataComment.UserCommentType = dataModel.UserCommentTypes.FirstOrDefault(t => t.ID == commentTypeID); if (comment.CheckinStatusType != null) { dataComment.CheckinStatusType = dataModel.CheckinStatusTypes.FirstOrDefault(t => t.ID == comment.CheckinStatusType.ID); } dataComment.UserName = comment.UserName; dataComment.Rating = comment.Rating; dataComment.RelatedURL = comment.RelatedURL; dataComment.DateCreated = DateTime.UtcNow; if (user != null && user.ID > 0) { var ocmUser = dataModel.Users.FirstOrDefault(u => u.ID == user.ID); if (ocmUser != null) { dataComment.User = ocmUser; dataComment.UserName = ocmUser.Username; } } /*if (dataComment.User==null) * { * return -3; //rejected, not authenticated * }*/ try { dataComment.ChargePoint.DateLastStatusUpdate = DateTime.UtcNow; dataModel.UserComments.Add(dataComment); dataModel.SaveChanges(); if (user != null) { AuditLogManager.Log(user, AuditEventType.CreatedItem, "Added Comment " + dataComment.ID + " to OCM-" + cpID, null); //add reputation points new UserManager().AddReputationPoints(user, 1); } //SendPOICommentSubmissionNotifications(comment, user, dataComment); //TODO: only refresh cache for specific POI CacheManager.RefreshCachedPOI(dataComment.ChargePoint.ID); return(dataComment.ID); } catch (Exception) { return(-2); //error saving } }