Update user's email in Orchard - database

I want to allow users to update their email, so I have controller :
[HttpPost]
public ActionResult ChangeEmail(string newEmail) {
IUser user = _services.WorkContext.CurrentUser;
if (!user.Is<UserPart>())
throw new InvalidCastException();
var userRecord = user.As<UserPart>().Record;
userRecord.Email = newEmail;
return null;
}
Everything builds and runs OK except that the database doesn't update new email.
What should I do ?
Thanks all !

I think your problem is that you are trying to update the Record directly instead of setting the value of the Part and letting Orchard save your changes:
user.As<UserPart>().Email = newEmail;
Your change will be automatically committed to the db at the end of the request.
As a side note the reason your changes are not saving is that when you update a record you need explicitly update it using an injected repository e.g. _userRepository.Update(user.Record) where _userRepository is an injected IRepository<UserPartRecord>, but just updating the part is the way to go.

Related

Entity Framework doesn't alter database

I'm new to Entity frameworks and got some questions. I've got an EFcore project where there needs to be a database around users, with roles, groups,..
First I had Razor CRUD pages where everything worked. I could add, update and delete users, roles, groups,... But along the way I realised that I rahter needed a SwaggerUI so I could use that API for an other frontend project.
So I changed the razor pages to Swagger and for some reason the database doesn't change when I Update, delete or post something. Without any warnings. I even get succes codes back as feedback.
But the action doesn't really go through.
(When I delete, it says deteled but record is still the same. Same with Update and with a post, it says that the creation succeeded but the new record is not in my database.
I can view all records with Get & specific Records with Get:ID so I'm kind of lost why my update, post or delete action don't work.
I'm kind of new in this area so any feedback is much appreciated.
Thanks in advance.
Try to update/delete/post a record in db. Always gives
UserController : ( this worked with the Razor pages but not with the swagger page)
[HttpDelete("{id}")]
public ActionResult<User> Delete(int id)
{
var user = _userRepo.Get(id);
if(user != null)
{
_userRepo.Delete(id);Console.WriteLine("is deleted.");
}
else
{
return NotFound();
}
return NoContent();
}
[HttpPost()]
public IActionResult Add([FromBody] UserCreateViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var newUser = new User
{
FirstName = model.FirstName,
LastName = model.LastName,
Email = model.Email,
Platform = model.Platform,
Is_enabled = model.Is_enabled,
};
Console.WriteLine(newUser);
_userRepo.Add(newUser);
return CreatedAtAction(nameof(Get), new { newUser.Id }, newUser);
}
Note: The Console.WriteLine("is deleted."); does run and is shown in the console. But it doesn't delete the record.
In Entity Framework, the SaveChanges() method internally creates a
transaction and wraps all INSERT, UPDATE and DELETE operations under
it. Multiple SaveChanges() calls, create separate transactions,
perform CRUD operations and then commit each transaction.
https://www.entityframeworktutorial.net/entityframework6/transaction-in-entity-framework.aspx#:~:text=In%20Entity%20Framework%2C%20the%20SaveChanges,and%20then%20commit%20each%20transaction.

Merge fields won't show on visualforce page

I just started learning Apex recently, and there's still a lot of topics that are hard for me to navigate at this time. I've searched everywhere for a solution that works, but I still haven't been able to figure it out.
I've created a button on my Salesforce org that renders a PDF from a visualforce page, and attaches it to the record as a File. This is to be used with Docusign later on to capture signatures for contracts. The problem is that, when using merge fields in the VF page, they either do not show at all, or I get this exception: "sObject row was retrieved via SOQL without querying the requested field".
Now, the exception explicitly says that I need to query the fields, and this is what I've found I need to do to make this work, but I have not been able to figure out how to do this properly. I've tried running a query in several places in my controller extension to no avail (I am using a standardController that SF created for my custom object).
Here's my extension's code:
public class attachPDFToQuote {
public final i360__Quote__c q {get; set;} //Quote object
//constructor
public attachPDFToQuote (ApexPages.StandardController stdController) {
q = (i360__Quote__c)stdController.getRecord();
/* for(i360__Quote__c query:[SELECT Id, Correspondence_Name__c, Name FROM i360__Quote__c WHERE Id=: q.Id]){
System.debug(i360__Quote__c.Correspondence_Name__c);
}*/
}
public PageReference attachPDF() {
/* for(i360__Quote__c query:[SELECT Id, Correspondence_Name__c, Name FROM i360__Quote__c WHERE Id=: q.Id]){
System.debug(i360__Quote__c.Correspondence_Name__c);
}*/
//generate and attach the PDF document
PageReference pdfPage = Page.ProjectAgreement;
Blob pdfBlob; //create a blob for the PDF content
if (!Test.isRunningTest()) { //if we are not in testing context
pdfBlob = pdfPage.getContent(); //generate the pdf blob
} else { //otherwise, we are in testing context. Create the blob manually.
pdfBlob = Blob.valueOf('PDF');
}
ContentVersion cvAttach = new ContentVersion(ContentLocation= 'S');
cvAttach.PathOnClient= 'Project Agreement.pdf';
cvAttach.Title= 'Project Agreement';
cvAttach.VersionData= pdfBlob;
insert cvAttach;
Id conDoc = [SELECT ContentDocumentID FROM ContentVersion WHERE Id=: cvAttach.Id].ContentDocumentId;
ContentDocumentLink ConDocLink = new COntentDocumentLink();
conDocLink.LinkedEntityId= q.Id;
conDocLink.ContentDocumentId= conDoc;
conDocLink.ShareType= 'V';
insert conDocLink;
//redirect the user
PageReference pageWhereWeWantToGo = new ApexPages.StandardController(q).view(); //redirect the User back to the Quote detail page
pageWhereWeWantToGo.setRedirect(true); //indicate that the redirect should be performed on the client side
return pageWhereWeWantToGo; //send the User on their way
}
}
I kept the commented code where I try to query the object fields so they show in VF. I also tried a couple of different ways, but nothing seems to work. Please let me know if I need to add anything else.
Thank you!
You didn't post your Visualforce page's code.
Even if it's same page (if your apex class is used in ProjectAgreement VF as <apex:page standardController="i360__Quote__c" extensions="attachPDFToQuote" - the act of grabbing a PDF version of the page counts as callout, a separate http traffic to fresh instance of the page so to speak.
So I suspect you need something like
PageReference pdfPage = Page.ProjectAgreement;
pdfPage.getParameters().put('id', q.Id);
Blob = pdfPage.getContent();
If that works... next step would be to look at your VF code.
If the page has merge fields such as {!i360__Quote__c.Name}, {!i360__Quote__c.Correspondence_Name__c} then magic should happen. Salesforce should figure out which fields are needed by looking at your VF page and silently query them for you. So you wouldn't even need the query in your constructor, you could just save stdController.getId() to class variable and then use that id in pdfPage.getParameters().set(...)
But if your VF page has references to {!quote.Correspondence_Name__c} then you need to keep the explicit query in there.

Laravel 8 Fortify User UUID Login Problem

I am currently setting up a new project using Laravel 8. Out of the box, Laravel is configured to use auto-incrementing ID's for the user's ID. In the past I have overrode this by doing the following.
Updating the ID column in the user table creation migration to
$table->uuid('id');
$table->primary('id');
Adding the following trait
trait UsesUUID
{
protected static function bootUsesUUID()
{
static::creating(function ($model) {
$model->{$model->getKeyName()} = (string) Str::orderedUuid();
});
}
}
Adding the following to the user model file
use UsesUUID;
public $incrementing = false;
protected $primaryKey = 'id';
protected $keyType = 'uuid';
On this new project, I did the same as above. This seems to break the login functionality. When the email and password are entered and submitted, the form clears as though the page has been refreshed. Thing to note is there are no typical validation error messages returned as would be expected if the email and/or password is wrong.
To check that the right account is actually being found and the password is being checked properly, I added the following code to the FortifyServiceProvider boot method. The log file confirms that the user is found and the user object dump is correct too.
Fortify::authenticateUsing(function(Request $request) {
\Log::debug('running login flow...');
$user = User::where('email', $request->email)->first();
if ($user && Hash::check($request->password, $user->password)) {
\Log::debug('user found');
\Log::debug($user);
return $user;
}
\Log::debug('user not found');
return false;
});
Undoing the above changes to the user model fixes the login problem. However, it introduces a new problem that is the login will be successful but it wont be the right account that is logged in. For example, there are 3 accounts, I enter the credentials for the second or third account, but no matter what, the system will always login using the first account.
Anyone have any suggestions or ideas as to what I may be doing wrong, or if anyone has come across the same/similar issue and how you went about resolving it?
Thanks.
After digging around some more, I have found the solution.
Laravel 8 now stores sessions inside the sessions table in the database. The sessions table has got a user_id column that is a foreign key to the id column in the users table.
Looking at the migration file for the sessions table, I found that I had forgot to change the following the problem.
From
$table->foreignId('user_id')->nullable()->index();
To
$table->foreignUuid('user_id')->nullable()->index();
This is because Laravel 8 by default uses auto incrementing ID for user ID. Since I had modified the ID column to the users table to UUID, I had forgotten to update the reference in the sessions table too.

Log changes detected to domaincontext based on haschanges and getchanges

I have a Silverlight application that is using Entity Framework 4. In the app, it is possible for the user to add/remove string representations of Active Directory group names into the configuration - changes are not saved in the backend until the 'save' button is clicked.
When 'save' is clicked, Entity Framework updates the backend with the changes to the DomainContext. This is working as expected. But I want to log the changes being made and send them out in an email each time before context.SubmitChanges() fires. What is the easiest way to log the changes? I already have code that I can reuse to email the changes to be logged.
I am looking at context.ADGroupRules.EntityContainer.GetChanges() and can see AddedEntities and RemovedEntities properties in there but I'm not sure how to 'get at' the highlighted string in the included snip in order to log it.
if (command == "Save")
{
if (_context.HasChanges)
{
var changeSet = _context.ADGroupRules.EntityContainer.GetChanges();
//log and email changes here
_context.SubmitChanges(OnSubmitCompleted, null);
}
}
I have figured it out.
if (command == "Save")
{
if (_context.HasChanges)
{
var changeSet = _context.ADGroupRules.EntityContainer.GetChanges();
foreach (var entity in changeSet.AddedEntities.OfType<ADGroupRule>())
{
ADGroupRule rule = (ADGroupRule)entity;
Console.WriteLine(rule.ADGroupName);//simulate logging-emailing
}

Persisting a backbone model and the id

Let's assume we create a new model class an instanciate a person using it:
var User = Backbone.Model.extend({
urlRoot: '/user'
});
var nou = new User({
name: "nourdine"
});
Now, of course, we want to persist it. Not having added an id backbone will create a POST request and communicate the server the intention to create an entity under /user containing the data {name: "nourdine"}. Here's how we do it:
nou.save(null, {
success: function (model, response, options) {
// ... what do I do here?
}
})
The server will now create a record in the db containing the JSON data rearranged in some form and assign an ID to it. NOW:
1 - What is the server suppose to return in the HTTP response? A JSON containing the JSON provided by the clinet + the newly created fields, namely the ID of the new record?
2 - who is going to update the model in the client with these data? Me? Matter of fact I would like to tell the model in the client that a new ID has been assign to it by the server so that the next time I do user.save() I will obtain a PUT rather than a POST. But who is supposed to update the model in the client?
Thanks
so this is my work flow for this
client -> create model and populate with data
client -> save model (model.save())
server -> create server side version of model using data, assign an id
server -> respond with success and the id of the newly created model
client -> in the success set the id to the one passed back
now the only potential issue i have with my work flow is if something did not get set in the server successfully but the model was still created, my client model would not reflect that of the server anymore, but i minimize this by returning error if the model could not be created exactly as passed.
And now i am able to call model.save() again this time having the id so initiating a PUT request
From the documentation to a Backbone.Model
After a successful server-side save, the client is (optionally) updated with the server-side state.
So if you return a valid JSON your model will be updated automatically

Resources