Saving user data when application closes in Flutter - flutter

I want to automatically save the username and password of password when he connects to the login page of my app, to then autofill the username and password field with the data in the next connection.
To do this I use a class which I called ContextManager where I store data I need to use in my app. I also use SharedPreferences.
Here is the code which is executed when the user log in to the app :
if (ContextManager.isConnected == false) {
return 'Le nom d\'utilisateur ou le mot de passe est incorrect';
} else {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', data.name);
prefs.setString('password', data.password);
ContextManager.savedEmail = prefs.getString('email').toString();
ContextManager.savedPassword = prefs.getString('password').toString();
return null;
}
And then in the build of my LoginPage widget I return a FlutterLogin widget (from the flutter_login package) with the value ContextManager.savedPassword for his savedPassword attribute, and ContextManager.SavedEmail for his savedEmail attribute.
It works when the app is running and I'm logging out while still in the app, the data is stored and autofilled correctly, but when I'm stopping the app and running it back all data is gone.
How could I permanently stored the data and simply retrieve it in the app at any moment ?
Thanks.

From my experience, I know three different packages to store data between app restart.
The first I use is the package sqflite, which, as the name suggests, includes a full SQLite database (but I don't know if that fits your definition of "simply retrieve it").
A simpler tool is the shared_preferences package, that allows you to store data as a key-value pairs(stored somewhere in a file unreachable by the user, if I'm correct).
The last one that I know of, that I used for a desktop app, is the ini package, that lets you create a configuration file like the .ini files often found in desktop applications. It's less user-friendly than the other two, but it allows you to choose where to put the file, and lets the user access that file if needed.

Related

Change flutter-redux store from deep inside a widget tree

In my app, the user can open a load a new file and replace the current data model.
Therefore, the app needs to update the outermost flutter-redux store by the just loaded new data model.
As I tried to replace the data just from the callback deep inside the widget tree, flutter reported an error. I suppose due to changed data during widget rendering.
Therefore, I changed the data model update of the store using WidgetsBinding.instance?.addPostFrameCallback:
if ( MainReducerHelper.getNewModelCallback is Function ) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
MainReducerHelper.getNewModelCallback( action.newModel );
});
}
Note, that MainReducerHelper.getNewModelCallback provides a callback, that allows to replace the flutter-redux store of the data model. The callback gets created alongside the first creation of the store.
While that works just fine, I'm not sure, if this is the canonical / right way to do this task.
If not, could you propose a better way? Or just confirm my approach?

Passing a selected database to a DBcontext via DI

Here is my scenario. Imagine a screen with a dropdown of US states. This list is populated from one Admin database. Depending on the choice of other items on the screen get filled with other databases. we have a database per state that share a single schema. I don't have any issue using DI for the States dropdown. However, I am having issues with getting the selected state. I tested hardcoding a state and DI works fine. I would like to use Session for this, but I have read you can't and frankly, I have not been able to make it work. Any suggestions would be appreciated.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped(p => p.GetService<IHttpContextAccessor>()?.HttpContext);
services.AddDbContext<AdminManagement.Data.AdminDataContext>(options =>
options.UseSqlServer(Configuration.GetSection("Connections:myAdmin").Value).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
//this is the issue here I want to be able to pass the selected state
services.AddDbContext<CollectionDataContext>((serviceProvider, builder) =>
{
//I wish I could use this...any alternatives?
//HttpContext.Session.GetString("SelectedState");
//hardcoded for testing purposes. it works ok
var selectedDb = "SC";
//this gets the connection string from app settings, later I will get it from an API
var connectionString = GetConnectionStringFromService(selectedDb);
builder.UseSqlServer(connectionString);
});
//my one admin database Data context
services.AddScoped<AdminManagement.Data.AdminManagementQueries>();
// my multiple databases clases that use DI
services.AddScoped<CollectionManagementQueries>();
services.AddScoped<CollectionManagementCommands>();
You need to retrieve the context from the service provider. That's done via:
var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
Then, you can do something like:
var selectedDb = httpContextAccessor.HttpContext.Session.GetString("SelectedState");
Note, however, that IHttpContextAccessor is not registered by default. You can fix that by adding the following in ConfigureServices:
services.AddHttpContextAccessor();

How can I create and update pages dynamically in Sulu CMS?

I have the following situation:
A database stores information about houses (address, number of rooms, date built, last selling price, etc.)
This database is being manipulated through an app (let's call that app the "backend house app") that cannot be directly integrated in a Sulu-driven app. I can access the stored data through an API that gives me JSON-representations of House-objects. I can also have the app launch some sort of call to a Sulu-driven app when a house is created, updated or deleted.
The Sulu-driven app (let's call that the "frontend house app") with templates for "house", "room", etc., is connected to a different database on a different server. This Sulu-driven app's website-environment shows house-pages with room-pages where some content is pre-filled through a connection to the "backend house app". Other content only exists on the database of the "frontend house app", like user comments, appraisals of interior design, etc., according to configured aspects of the Sulu-templates.
What I want to achieve, is a way to automate the creation, updating and deletion of "frontend house app"-pages based on activity in the "backend house app".
For instance, when a new house is added in the "backend house app", I want it to notify the "frontend house app" so that the "frontend house app" will automatically create the entire node-tree for the newly added house. Meaning: a "house"-page with the required data filled in, "room"-pages for each room, etc., so that the content manager of the "frontend house app" can see the entire tree of the newly added house in the workspace and can start manipulating content in the already available templates. In addition to automatically creating these pages, I also want to pre-set the rights to update and create, since the content manager of the "frontend house app" must not be able to create new rooms or change the name of the house, for instance.
I did not manage to get it working, I'll just add what I already done to show where I got stuck.
I started out with the following code, in a controller that extends Sulu's own WebsiteController:
$documentManager = $this->get('sulu_document_manager.document_manager');
$nodeManager = $this->get('sulu_document_manager.node_manager');
$parentHousesDocument = $documentManager->find('/cmf/immo/routes/nl/huizen', 'nl');
$newHouseDocument = $documentManager->create('page');
// The backendApi just gives a House object with data from the backend
// In this case we get an existing House with id 1
$house = $backendApi->getHouseWithId(1);
$newHouseDocument->setTitle($house->getName()); // For instance 'Smurfhouse'
$newHouseDocument->setLocale('nl'); // Nl is the only locale we have
$newHouseDocument->setParent($parentHouseDocument); // A default page where all the houses are listed
$newHouseDocument->setStructureType('house'); // Since we have a house.xml template
// I need to grab the structure to fill it with values from the House object
$structure = $newHouseDocument->getStructure();
$structure->bind([
'title' => $house->getName(),
'houseId' => $house->getId(),
]);
$newHouseDocument->setWorkflowStage(WorkflowStage::PUBLISHED); // You would expect this to automatically publish the document, but apparently it doesn't... I took it from a test I reverse-engineered in trying to create a page, I have no clue what it is supposed to change.
$nodeManager->createPath('/cmf/immo/routes/nl/huizen/' . $house->getId());
$documentManager->persist(
$newHouseDocument,
'nl',
[
'path' => '/cmf/immo/contents/huizen/' . Slugifier::slugify($house->getName()), // Assume for argument's sake that the Slugifier just slugifies the name...
'auto_create' => true, // Took this value from a test that creates pages, don't know whether it is necessary
'load_ghost_content' => false, // Idem
]
);
$documentManager->flush();
Now, when I fire the controller action, I first get the exception
Property "url" in structure "house" is required but no value was given.
I tried to fix this by just manually binding the property 'url' with value '/huizen/' . $house->getId() to $structure, at the point where I bind the other values. But this doesn't fix it, as apparently the url value is overwritten somewhere in the persist event chain, and I haven't yet found where.
However, I can, just for testing purposes, manually override the url in the StructureSubscriber that handles the mapping for this particular persist event. If I do this, something gets created in the Sulu-app-database - hurray!
My phpcr_nodes table lists two extra records, one for the RouteDocument referring to /cmf/immo/routes/nl/huizen/1, and one for the PageDocument referring to /cmf/immo/contents/huizen/smurfhouse. Both have the workspace_name column filled with the value default_live. However, as long as there are not also records that are complete duplicates of these two records except with the value default in the workspace_name column, the pages will not appear in the Sulu admin CMS environment. Needless to say, they will also not appear on the public website proper.
Furthermore, when I let the DocumentManager in my controller action try to ->find my newly created document, I get a document of the class UnknownDocument. Hence, I cannot have the DocumentManager go ->publish on it; an Exception ensues. If I visit the pages in the Sulu admin environment, they are hence unpublished; once I publish them there, they can be found by the DocumentManager in the controller action - even if I later unpublish them. They are no longer UnknownDocument, for some reason. However, even if they can be found, I cannot have the DocumentManager go ->unpublish nor ->publish - that just has NO effect on the actual documents.
I was hoping there would be a Sulu cookbook-recipe or another piece of documentation that extensively describes how to create fully published pages dynamically, thus without going through the 'manual labor' of the actual CMS environment, but so far I haven't found one... All help is much appreciated :)
PS: For the purposes of being complete: we're running Sulu on a Windows server environment on PHP 7.1; dbase is PostgreSQL, Sulu being a local forked version of release tag 1.4.7 because I had to make some changes to the way Sulu handles uploaded files to get it to work on a Windows environment.
EDIT: a partial solution for making a new house page if none exists already (not explicitly using the AdminKernel, but should of course be run in a context where the AdminKernel is active):
public function getOrCreateHuisPagina(Huis $huis)
{
$parent = $this->documentManager->find('/cmf/immo/routes/nl/huizen', 'nl'); // This is indeed the route document for the "collector page" of all the houses, but this doesn't seem to give any problems (see below)
try {
$document = $this->documentManager->find('/cmf/immo/routes/nl/huizen/' . $huis->id(), 'nl'); // Here I'm checking whether the page already exists
} catch(DocumentNotFoundException $e) {
$document = $this->setupPublishedPage();
$document->setTitle($huis->naam());
$document->setStructureType('huis_detail');
$document->setResourceSegment('/huizen');
$document->setParent($parent);
$document->getStructure()->bind([
'title' => $huis->naam(), // Not sure if this is required seeing as I already set the title
'huis_id' => $huis->id(),
]);
$this->documentManager->persist(
$document,
'nl',
[
'parent_path' => '/cmf/immo/contents/huizen', // Explicit path to the content document of the parnt
]
);
}
$this->documentManager->publish($document, 'nl');
return $document;
}
First of all I think the following line does not load what you want it to load:
$parentHousesDocument = $documentManager->find('/cmf/immo/routes/nl/huizen', 'nl');
It loads the route instead of the page document, so it should look like the following:
$parentHousesDocument = $documentManager->find('/cmf/immo/contents/nl/huizen', 'nl');
Regarding your error with the URL, instead of overriding the StructureSubscriber you should simple use the setResourceSegment method of the document, which does exactly what you need :-)
And the default_live workspace is wrong, is it possible that you are running these commands on the website kernel? The thing is that the WebsiteKernel has the default_live workspace as default, and therefore writes the content in this workspace. If you run the command with the AdminKernel it should land in the default workspace, and you should be able to copy it into the default_live workspace with the publish method of the DocumentManager.
I hope that helps :-)

How to remove presentity users entry in kamailio server?

I found that in:
kamctl db show presentity
Is the presence information of the users, but how can I errase such entry? of a user?
And how can I define only one entry in that database by user?
I use this code, when I receive a publish but this adds a new entry every time.
if(is_method("PUBLISH"))
{
if($hdr(Sender)!= NULL)
handle_publish("$hdr(Sender)");
else
handle_publish();
t_release();
}
The idea is have the possibility of a user publish multiple times, but have just one entry.
You can use this: https://www.kamailio.org/docs/modules/stable/modules/presence.html#presence.p.xavp_cfg
To order by timestamp you just need this line modparam("presence", "xavp_cfg", "pres").
This way, I think that your problem is solved.
Let me know if it's result.

Liferay: how to save to portlet user information?

I have at the welcome page a weather portlet, and user can configure the portlet and select his city. Is it possible to store user information in the portlet preferences, so that every user has his one stored city? Or what is the standard workflow to store user-portlet information without to develop own (persist) service?
thx
The portlet-preferences are in liferay per default not user specific. That can be modified in liferay-portlet.xml with next lines:
<liferay-portlet-app>
<portlet>
<portlet-name>ThePortletWitchUserSpecificPreferences</portlet-name>
<icon>/icon.png</icon>
<preferences-unique-per-layout>false</preferences-unique-per-layout>
<preferences-owned-by-group>false</preferences-owned-by-group>
</portlet>
...
</liferay-portlet-app>
the two lines <preferences-... and the order are abbreviated.
for more information see:
http://rutvijshah.wordpress.com/2009/12/06/user-specific-preferences-in-liferay/
It's not a native function of the PortletPreference : the setValue method allow only a String, unfortunately you can't pass a Map.
However, i see a solution to hardcode it, but it's a little bit ugly...
Long userId = ...... ;
String userValue = ..... ;
PortletPreferences prefs = request.getPreferences();
prefs.setValue("myConfig-"+userId, myUserVal);
prefs.store();
And for retrieve the data :
String userValue = prefs.getValue("myConfig-"+userId, defaultValue);
This solution will work, but don't do that is you have a big numbers of users.
Portlet Preferences are save in xml in your database, if you have 100k+ users, it will explode :)
If you think this solution is not enough clean, you will have to create your own persistence method with the ServiceBuilder.