Skip to content
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

Projects in PostgreSQL #6752

Merged
merged 20 commits into from
Apr 9, 2018
Merged

Conversation

wonder-sk
Copy link
Member

This introduces the possibility to load/save QGIS projects inside PostgreSQL databases.

More details about the internals in the QEP: qgis/QGIS-Enhancement-Proposals#118

In Project menu, there are two new menu items:

  • Load From
  • Save To

Both menu items have a sub-menu with list of extra project storage implementations - currently just PostgreSQL. Clicking the action will open a dialog to pick PostgreSQL connection name, schema name and project.

Projects stored in PostgreSQL can be also loaded from browser panel (their entries are located within the schema they are stored in) - either by double-clicking them or by dragging them to main canvas.

Developed for Arpa Piemonte (Dipartimento Tematico Geologia e Dissesto) within ERIKUS project.


virtual bool removeProject( const QString &uri ) = 0;
%Docstring
Removes and existing project at the given URI. Returns true if the removal
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An

was successful.
%End

virtual bool readProjectMetadata( const QString &uri, QgsProjectStorage::Metadata &metadata /Out/ );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this name could easily get mistaken for dealing with QgsProjectMetadata objects. Maybe change it to readProjectStorageMetadata instead?

%End
public:
QString name;
QDateTime lastModified;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use a variant for lastModified checks and storage - it's possible some providers won't have access to a last modified time and may instead need to use a version number or project hash instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really prefer to stick with QDateTime - if there would be a storage implementation that can't support last modified time, I would prefer to introduce some other member(s) to accommodate more kinds of metadata (or just a QVariantMap for any extra stuff). Some implementations would keep the time value invalid in the worst case... Would that work for you? I am trying to avoid variants as much as possible as they suddenly make all the nice c++ type checks disappear...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be happy to see a QVariantMap added, but that kind-of goes against your desire to avoid variants. I guess in either case we need a bool QgsProjectStorage::hasProjectChanged( const QgsProjectStorage::Metadata& metadata ) member in QgsProjectStorage, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would need a hasProjectChanged() virtual method only if we were to introduce support for project storage implementation that cannot store last modified time for some reason. For now QGIS app just compares timestamps - in the future when needed the extension to use hasProjectChanged() should be fairly straightforward...

void QgisApp::populateProjectStorageMenu( QMenu *menu, bool saving )
{
menu->clear();
for ( QgsProjectStorage *storage : QgsApplication::projectStorageRegistry()->projectStorages() )
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will detach (I suggest using qt creator 4.6 and enable clazy warnings)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the tip... looks like I should upgrade!

if ( QgsProject::instance()->write() )
{
setTitleBarText_( *this ); // update title bar
mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( uri ), 5000 );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this get a bit annoying? I save every 10 seconds sometimes :p

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the same kind of code in QgisApp::fileSave() and QgisApp::fileSaveAs() so I just brought it here as well for consistency... I am happy to remove it if you prefer - but this is just status bar text update - not a popup in the message bar

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - I read that wrong and thought it was message bar. In that case no issue here!

* Registry of storage backends that QgsProject may use.
* This is a singleton that should be accessed through QgsApplication::projectStorageRegistry().
*
* \since QGIS 3.2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ingroup core

class QgsPostgresProjectStorage : public QgsProjectStorage
{
public:
QgsPostgresProjectStorage();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= default


private:

bool mSaving; //!< Whether using this dialog for loading or saving a project
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialize

@@ -42,15 +42,27 @@
<string>New From Template</string>
</property>
</widget>
<widget class="QMenu" name="mProjectToStorageMenu">
<property name="title">
<string>Save To</string>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly certain HIG guidelines is not to capitalise to/from

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got inspiration from the existing "New From Template" string - going to change that one as well

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - I tried to catch a lot of these a week or so ago, but I've obviously missed some.

QCOMPARE( prj1.baseName(), QString( "project1" ) );
QVERIFY( prj1.lastModified().secsTo( QDateTime::currentDateTime() ) < 1 );

// read the project back
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test here which uses zipped contents too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - added in b8a9329

@nyalldawson
Copy link
Collaborator

Looks great!

One thing I'd like to see is a way to disable storage for specific postgres connections - I can see cases where admins would want postgres connections to be used for data only and not expose this new functionality to certain connections. Maybe a checkbox in the connection properties? (And maybe default to off for safety?)

luipir
luipir previously requested changes Apr 6, 2018
QString username = u.userName();
QString password = u.password();
QgsDataSourceUri::SslMode sslMode = QgsDataSourceUri::SslPrefer;
QString authConfigId; // TODO: ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see blacklisted tests to manage authConfigId to integrate QGIS Auth infrastructure instead to use only basci auth.
btw in this moment it can be tested because we don't have any pki enable docker to test against (I'm slowly working on that when I've some spare time, sorry)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @luipir this is an older commit, the todo has been addressed already

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ho sorry, I just saw it in c765295

@wonder-sk
Copy link
Member Author

@nyalldawson Thanks a lot for the review!

One thing I'd like to see is a way to disable storage for specific postgres connections - I can see cases where admins would want postgres connections to be used for data only and not expose this new functionality to certain connections.

I would prefer that admins set up the permissions in the database rather than inside QGIS:

  • in QGIS users could still easily change the connection config, ignoring admin's policy
  • in QGIS the permissions would be very coarse, while in the database admins can manage permissions with much greater control, e.g. allow loading/saving of projects only in some schemas, allow only some users/groups to use the projects, make projects read-only by default, ...
  • for postgres we already create/use table layer_styles (and more could come, e.g. metadata table) which may also need a similar enable/disable option for consistency (but as said, I think this should be configured in postgres permissions)

@wonder-sk
Copy link
Member Author

Everything from the review should be addressed now...

@nyalldawson
Copy link
Collaborator

I would prefer that admins set up the permissions in the database rather than inside QGIS:

I totally agree that this is a critical part of the correct approach, but,...

in QGIS users could still easily change the connection config, ignoring admin's policy
in QGIS the permissions would be very coarse, while in the database admins can manage permissions with much greater control, e.g. allow loading/saving of projects only in some schemas, allow only some users/groups to use the projects, make projects read-only by default, ...
for postgres we already create/use table layer_styles (and more could come, e.g. metadata table) which may also need a similar enable/disable option for consistency (but as said, I think this should be configured in postgres permissions)

What I want to avoid is a feature being exposed in the ui which has been blocked on the database (via postgres permissions) and is not desired in an organisation. (I'd present the same argument if the database style store feature was a new PR too! That should also have a way to disable per connection).

I've got a couple of organisations I maintain where I would not want to see them using this feature (for various internal IT/maintenance reasons, not because of stability of your code or anything!) and for that reason I'd like to be able to disable this option for the organisation's data store via the scripts which setup the connection. I just know that there's users who will click these options and try to use it just because it's there... and I'd prefer that the option was disabled in the UI as opposed to users hitting an unfriendly database permission issue.

I think it's also a bit risky to suddenly add this to existing installs/connections - organisation's db/QGIS maintainers may not be aware that on installing 3.2 this option will suddenly become possible and their users can start storing projects in the databases, possibly before any proper backup/policies/etc in place. So my preference would be adding a checkbox for this, which defaults to disabled for existing connections but defaults to enabled when creating a new connection.

@NathanW2
Copy link
Member

NathanW2 commented Apr 7, 2018 via email

@wonder-sk
Copy link
Member Author

What I want to avoid is a feature being exposed in the ui which has been blocked on the database (via postgres permissions) and is not desired in an organisation.

I see your point and I do not really have a strong opinion about that, so let's have a per-connection opt-in configuration option for that... I assume you meant that users would not be able to write nor read projects in such connections (even if the qgis_projects table existed), right?

@wonder-sk
Copy link
Member Author

Rebased, Nyall's comments addressed in 42969a4 (variables) and e80f604 (enable/disable postgres projects storage)

@wonder-sk wonder-sk merged commit f378a23 into qgis:master Apr 9, 2018
@NathanW2
Copy link
Member

NathanW2 commented Apr 9, 2018 via email

@nyalldawson
Copy link
Collaborator

@wonder-sk

Found some issues while testing this today:

  1. If you try to open a project from an unavailable connection (e.g. from recent projects list), you get a crash
  2. QgsPostgresProjectStorage::readProject (and writeProject) pushes handy errors to the read write context, but these aren't used anywhere, and instead a generic "Unable to open %1" error is used instead. It would be better to pop the last error from the context here and use the more descriptive error instead.
  3. QgsPostgresProjectStorage::readProject and writeProject have a bunch of untranslated strings when errors are pushed to the context.

@wonder-sk
Copy link
Member Author

Thanks for testing @nyalldawson - fixed in 4a885e6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants