Skip to content

Commit

Permalink
[FEATURE] Expression variables relating to QGIS environment
Browse files Browse the repository at this point in the history
New variables for:
- @qgis_os_name: eg 'linux','windows' or 'osx'
- @qgis_platform: eg 'desktop' or 'server'
- @user_account_name: current user's operating system account name
- @user_full_name: current user's name from os account (if available)

Sponsored by Andreas Neumann
  • Loading branch information
nyalldawson committed Jan 15, 2016
1 parent 5f3ca88 commit 91f3005
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 5 deletions.
26 changes: 25 additions & 1 deletion python/core/qgsapplication.sip
Expand Up @@ -228,9 +228,33 @@ static void qtgui_UpdatePyArgv(PyObject *argvlist, int argc, char **argv)
//! Returns the path to user's style.
static QString userStyleV2Path();

//! Returns the short name regular exprecience for line edit validator
//! Returns the short name regular expression for line edit validator
static QRegExp shortNameRegExp();

/** Returns the user's operating system login account name.
* @note added in QGIS 2.14
* @see userFullName()
*/
static QString userLoginName();

/** Returns the user's operating system login account full display name.
* @note added in QGIS 2.14
* @see userLoginName()
*/
static QString userFullName();

/** Returns a string name of the operating system QGIS is running on.
* @note added in QGIS 2.14
* @see platform()
*/
static QString osName();

/** Returns the QGIS platform name, eg "desktop" or "server".
* @note added in QGIS 2.14
* @see osName()
*/
static QString platform();

//! Returns the path to user's themes folder
static QString userThemesFolder();

Expand Down
101 changes: 100 additions & 1 deletion src/core/qgsapplication.cpp
Expand Up @@ -39,8 +39,14 @@

#ifndef Q_OS_WIN
#include <netinet/in.h>
#include <pwd.h>
#else
#include <winsock.h>
#include <windows.h>
#include <Lmcons.h>
#define SECURITY_WIN32
#include <Security.h>
#pragma comment( lib, "Secur32.lib" )
#endif

#include "qgsconfig.h"
Expand Down Expand Up @@ -72,6 +78,10 @@ QStringList ABISYM( QgsApplication::mGdalSkipList );
int ABISYM( QgsApplication::mMaxThreads );
QString ABISYM( QgsApplication::mAuthDbDirPath );

QString QgsApplication::sUserName;
QString QgsApplication::sUserFullName;
QString QgsApplication::sPlatformName = "desktop";

const char* QgsApplication::QGIS_ORGANIZATION_NAME = "QGIS";
const char* QgsApplication::QGIS_ORGANIZATION_DOMAIN = "qgis.org";
const char* QgsApplication::QGIS_APPLICATION_NAME = "QGIS2";
Expand All @@ -89,9 +99,11 @@ const char* QgsApplication::QGIS_APPLICATION_NAME = "QGIS2";
so that platform-conditional code is minimized and paths are easier
to change due to centralization.
*/
QgsApplication::QgsApplication( int & argc, char ** argv, bool GUIenabled, const QString& customConfigPath )
QgsApplication::QgsApplication( int & argc, char ** argv, bool GUIenabled, const QString& customConfigPath, const QString& platformName )

This comment has been minimized.

Copy link
@SebDieBln

SebDieBln Jan 16, 2016

Contributor

Should this change be reflected in the corresponding SIP file?

I have the same issue in my PR #2491, so I am looking forward to your answer.

: QApplication( argc, argv, GUIenabled )
{
sPlatformName = platformName;

init( customConfigPath ); // init can also be called directly by e.g. unit tests that don't inherit QApplication.
}

Expand Down Expand Up @@ -722,6 +734,93 @@ QRegExp QgsApplication::shortNameRegExp()
return QRegExp( "^[A-Za-z][A-Za-z0-9\\._-]*" );
}

QString QgsApplication::userLoginName()
{
if ( !sUserName.isEmpty() )
return sUserName;

#ifdef Q_OS_WIN
TCHAR name [ UNLEN + 1 ];
DWORD size = UNLEN + 1;

if ( GetUserName(( TCHAR* )name, &size ) )
{
sUserName = QString( name );
}

#else
QProcess process;

process.start( "whoami" );
process.waitForFinished();
sUserName = process.readAllStandardOutput().trimmed();
#endif

if ( !sUserName.isEmpty() )
return sUserName;

//backup plan - use environment variables
sUserName = qgetenv( "USER" );
if ( !sUserName.isEmpty() )
return sUserName;

//last resort
sUserName = qgetenv( "USERNAME" );
return sUserName;
}

QString QgsApplication::userFullName()
{
if ( !sUserFullName.isEmpty() )
return sUserFullName;

#ifdef Q_OS_WIN
TCHAR name [ UNLEN + 1 ];
DWORD size = UNLEN + 1;

//note - this only works for accounts connected to domain
if ( GetUserNameEx( NameDisplay, ( TCHAR* )name, &size ) )
{
sUserFullName = QString( name );
}

//fall back to login name
if ( sUserFullName.isEmpty() )
sUserFullName = userLoginName();
#else
struct passwd *p = getpwuid( getuid() );

if ( p )
{
QString gecosName = QString( p->pw_gecos );
sUserFullName = gecosName.left( gecosName.indexOf( ',', 0 ) );
}

#endif

return sUserFullName;
}

QString QgsApplication::osName()
{
#if defined(Q_OS_ANDROID)
return QLatin1String( "android" );
#elif defined(Q_OS_MAC)
return QLatin1String( "osx" );
#elif defined(Q_OS_WIN)
return QLatin1String( "windows" );
#elif defined(Q_OS_LINUX)
return QLatin1String( "linux" );
#else
return QLatin1String( "unknown" );
#endif
}

QString QgsApplication::platform()
{
return sPlatformName;
}

QString QgsApplication::userThemesFolder()
{
return qgisSettingsDirPath() + QLatin1String( "/themes" );
Expand Down
32 changes: 30 additions & 2 deletions src/core/qgsapplication.h
Expand Up @@ -38,7 +38,7 @@ class CORE_EXPORT QgsApplication : public QApplication
static const char* QGIS_ORGANIZATION_NAME;
static const char* QGIS_ORGANIZATION_DOMAIN;
static const char* QGIS_APPLICATION_NAME;
QgsApplication( int & argc, char ** argv, bool GUIenabled, const QString& customConfigPath = QString() );
QgsApplication( int & argc, char ** argv, bool GUIenabled, const QString& customConfigPath = QString(), const QString& platformName = "desktop" );
virtual ~QgsApplication();

/** This method initialises paths etc for QGIS. Called by the ctor or call it manually
Expand Down Expand Up @@ -190,9 +190,33 @@ class CORE_EXPORT QgsApplication : public QApplication
//! Returns the path to user's style.
static QString userStyleV2Path();

//! Returns the short name regular exprecience for line edit validator
//! Returns the short name regular expression for line edit validator
static QRegExp shortNameRegExp();

/** Returns the user's operating system login account name.
* @note added in QGIS 2.14
* @see userFullName()
*/
static QString userLoginName();

/** Returns the user's operating system login account full display name.
* @note added in QGIS 2.14
* @see userLoginName()
*/
static QString userFullName();

/** Returns a string name of the operating system QGIS is running on.
* @note added in QGIS 2.14
* @see platform()
*/
static QString osName();

/** Returns the QGIS platform name, eg "desktop" or "server".
* @note added in QGIS 2.14
* @see osName()
*/
static QString platform();

//! Returns the path to user's themes folder
static QString userThemesFolder();

Expand Down Expand Up @@ -378,6 +402,10 @@ class CORE_EXPORT QgsApplication : public QApplication
/**
* @note added in 2.12 */
static QString ABISYM( mAuthDbDirPath );

static QString sUserName;
static QString sUserFullName;
static QString sPlatformName;
};

#endif
4 changes: 4 additions & 0 deletions src/core/qgsexpression.cpp
Expand Up @@ -4432,6 +4432,10 @@ void QgsExpression::initVariableHelp()
gVariableHelpTexts.insert( "qgis_version", QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
gVariableHelpTexts.insert( "qgis_version_no", QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
gVariableHelpTexts.insert( "qgis_release_name", QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
gVariableHelpTexts.insert( "qgis_os_name", QCoreApplication::translate( "variable_help", "Operating system name, eg 'windows', 'linux' or 'osx'." ) );
gVariableHelpTexts.insert( "qgis_platform", QCoreApplication::translate( "variable_help", "QGIS platform, eg 'desktop' or 'server'." ) );
gVariableHelpTexts.insert( "user_account_name", QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
gVariableHelpTexts.insert( "user_full_name", QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );

//project variables
gVariableHelpTexts.insert( "project_title", QCoreApplication::translate( "variable_help", "Title of current project." ) );
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsexpressioncontext.cpp
Expand Up @@ -25,6 +25,7 @@
#include "qgscomposition.h"
#include "qgscomposeritem.h"
#include "qgsatlascomposition.h"
#include "qgsapplication.h"
#include <QSettings>
#include <QDir>

Expand Down Expand Up @@ -465,6 +466,10 @@ QgsExpressionContextScope* QgsExpressionContextUtils::globalScope()
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_version", QGis::QGIS_VERSION, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_version_no", QGis::QGIS_VERSION_INT, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_release_name", QGis::QGIS_RELEASE_NAME, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_platform", QgsApplication::platform(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_os_name", QgsApplication::osName(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( "user_account_name", QgsApplication::userLoginName(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( "user_full_name", QgsApplication::userFullName(), true ) );

return scope;
}
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgsserver.cpp
Expand Up @@ -330,7 +330,7 @@ bool QgsServer::init( int & argc, char ** argv )
QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, optionsPath );
}

mQgsApplication = new QgsApplication( argc, argv, getenv( "DISPLAY" ) );
mQgsApplication = new QgsApplication( argc, argv, getenv( "DISPLAY" ), QString(), "server" );

QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
Expand Down
33 changes: 33 additions & 0 deletions tests/src/core/testqgsapplication.cpp
Expand Up @@ -29,6 +29,11 @@ class TestQgsApplication: public QObject
void checkGdalSkip();
void initTestCase();
void cleanupTestCase();

void accountName();
void osName();
void platformName();

private:
QString getQgisPath();
};
Expand All @@ -50,6 +55,34 @@ void TestQgsApplication::cleanupTestCase()
QgsApplication::exitQgis();
}

void TestQgsApplication::accountName()
{
QString loginName = QgsApplication::userLoginName();
qDebug() << QString( "Got login name: '%1'" ).arg( loginName );
QVERIFY( !loginName.isEmpty() );
//test cached return works correctly
QCOMPARE( loginName, QgsApplication::userLoginName() );

//can't test contents, as it can be validly empty (eg on Travis). Just testing that we don't crash
QString fullName = QgsApplication::userFullName();
qDebug() << QString( "Got full name: '%1'" ).arg( fullName );
//test cached return works correctly
QCOMPARE( fullName, QgsApplication::userFullName() );
}

void TestQgsApplication::osName()
{
// can't test expected result, so just check for non-empty result
qDebug() << QString( "Got OS name: '%1'" ).arg( QgsApplication::osName() );
QVERIFY( !QgsApplication::osName().isEmpty() );
}

void TestQgsApplication::platformName()
{
// test will always be run under desktop platform
QCOMPARE( QgsApplication::platform(), QString( "desktop" ) );
}

void TestQgsApplication::checkPaths()
{
QString myPath = QgsApplication::authorsFilePath();
Expand Down
8 changes: 8 additions & 0 deletions tests/src/core/testqgsexpressioncontext.cpp
Expand Up @@ -488,10 +488,18 @@ void TestQgsExpressionContext::globalScope()
QgsExpression expVersion( "var('qgis_version')" );
QgsExpression expVersionNo( "var('qgis_version_no')" );
QgsExpression expReleaseName( "var('qgis_release_name')" );
QgsExpression expAccountName( "var('user_account_name')" );
QgsExpression expUserFullName( "var('user_full_name')" );
QgsExpression expOsName( "var('qgis_os_name')" );
QgsExpression expPlatform( "var('qgis_platform')" );

QCOMPARE( expVersion.evaluate( &context ).toString(), QString( QGis::QGIS_VERSION ) );
QCOMPARE( expVersionNo.evaluate( &context ).toInt(), QGis::QGIS_VERSION_INT );
QCOMPARE( expReleaseName.evaluate( &context ).toString(), QString( QGis::QGIS_RELEASE_NAME ) );
QCOMPARE( expAccountName.evaluate( &context ).toString(), QgsApplication::userLoginName() );
QCOMPARE( expUserFullName.evaluate( &context ).toString(), QgsApplication::userFullName() );
QCOMPARE( expOsName.evaluate( &context ).toString(), QgsApplication::osName() );
QCOMPARE( expPlatform.evaluate( &context ).toString(), QgsApplication::platform() );

//test setGlobalVariables
QgsStringMap vars;
Expand Down

0 comments on commit 91f3005

Please sign in to comment.