diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index b6b34e6b3c3..afc8eec3e27 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -22,7 +22,7 @@ ELSE(TXT2TAGS_EXECUTABLE)
)
ENDIF(TXT2TAGS_EXECUTABLE)
-SET(QGIS_DOC_FILES ${QGIS_DOC_FILES} index.html news.html developersmap.html contributors.json favicon.ico style.css release-sponsors.html AUTHORS CONTRIBUTORS SPONSORS DONORS TRANSLATORS LICENSE)
+SET(QGIS_DOC_FILES ${QGIS_DOC_FILES} index.html news.html developersmap.html nohelp.html contributors.json favicon.ico style.css release-sponsors.html AUTHORS CONTRIBUTORS SPONSORS DONORS TRANSLATORS LICENSE)
INSTALL(FILES ${QGIS_DOC_FILES} DESTINATION ${QGIS_DATA_DIR}/doc)
INSTALL(FILES ../images/icons/qgis-icon-60x60.png DESTINATION ${QGIS_DATA_DIR}/doc/images)
diff --git a/doc/nohelp.html b/doc/nohelp.html
new file mode 100644
index 00000000000..ff46d27cdc6
--- /dev/null
+++ b/doc/nohelp.html
@@ -0,0 +1,19 @@
+
+
+
+ No help found
+
+
+
+
+
+Oops!
+Sorry, help is not available. Probably you have no Internet connection,
+location of the help files is not configured or there is no help content
+for requested topic.
+Please make sure that correct location of the help files specified
+in the QGIS options dialog (Settings → Options →
+System).
+
+
+
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ecfc306243c..bcf06ac5a03 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -135,6 +135,7 @@ SET(QGIS_CORE_SRCS
qgsgeometryvalidator.cpp
qgsgml.cpp
qgsgmlschema.cpp
+ qgshelp.cpp
qgshistogram.cpp
qgsinterval.cpp
qgsjsonutils.cpp
@@ -481,6 +482,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsgeometryvalidator.h
qgsgml.h
qgsgmlschema.h
+ qgshelp.h
qgsmaplayer.h
qgsmaplayerlegend.h
qgsmaplayermodel.h
diff --git a/src/core/qgshelp.cpp b/src/core/qgshelp.cpp
new file mode 100644
index 00000000000..2fb71147b46
--- /dev/null
+++ b/src/core/qgshelp.cpp
@@ -0,0 +1,145 @@
+/***************************************************************************
+ qgshelp.cpp
+ --------------------------------------
+ Date : December 2016
+ Copyright : (C) 2016 by Alexander Bruy
+ Email : alexander dot bruy at gmail dot com
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "qgshelp.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "qgis.h"
+#include "qgsapplication.h"
+
+QgsHelp *QgsHelp::sHelp = nullptr; // Singleton instance
+
+void QgsHelp::openHelp( const QString& key )
+{
+ if ( !sHelp )
+ {
+ sHelp = new QgsHelp();
+ }
+
+ QDesktopServices::openUrl( sHelp->helpUrl( key ) );
+}
+
+QUrl QgsHelp::helpUrl( const QString& key )
+{
+ if ( !sHelp )
+ {
+ sHelp = new QgsHelp();
+ }
+
+ QSettings settings;
+ QUrl helpNotFound = QUrl::fromLocalFile( QgsApplication::pkgDataPath() + "/doc/nohelp.html" );
+
+ QString paths = settings.value( QStringLiteral( "help/helpSearchPath" ), "" ).toString();
+ if ( paths.isEmpty() )
+ {
+ return helpNotFound;
+ }
+
+ QString qgisLocale;
+ bool overrideLocale = settings.value( QStringLiteral( "locale/overrideFlag" ), false ).toBool();
+ if ( overrideLocale )
+ {
+ qgisLocale = settings.value( QStringLiteral( "locale/userLocale" ), "" ).toString();
+ }
+ else
+ {
+ qgisLocale = QLocale::system().name().left( 2 );
+ }
+
+ QString qgisVersion;
+ if ( Qgis::QGIS_VERSION_INT / 100 % 100 == 99 )
+ {
+ qgisVersion = QStringLiteral( "testing" );
+ qgisLocale = QStringLiteral( "en" );
+ }
+ else
+ {
+ qgisVersion = QStringLiteral( "%1.%2" ).arg( Qgis::QGIS_VERSION_INT / 10000 ).arg( Qgis::QGIS_VERSION_INT / 100 % 100 );
+ }
+
+ QString suffix = QStringLiteral( "%1/%2/docs/user_manual/%3" ).arg( qgisVersion ).arg( qgisLocale ).arg( key );
+
+ QUrl myUrl;
+ QString helpPath;
+ bool helpFound = false;
+
+ QStringList pathList = paths.split( '|' );
+ QStringList::const_iterator pathIt = pathList.constBegin();
+ for ( ; pathIt != pathList.constEnd(); ++pathIt )
+ {
+ helpPath = QStringLiteral( "%1/%2" ).arg( *pathIt ).arg( suffix );
+
+ if (( *pathIt ).startsWith( QStringLiteral( "http://" ) ) )
+ {
+ if ( !sHelp->urlExists( helpPath ) )
+ {
+ continue;
+ }
+ myUrl = QUrl( helpPath );
+ }
+ else
+ {
+ if ( !QFileInfo( helpPath.mid( 0, helpPath.lastIndexOf( "#" ) ) ).exists() )
+ {
+ continue;
+ }
+ myUrl = QUrl::fromLocalFile( helpPath );
+ myUrl.setFragment( helpPath.mid( helpPath.lastIndexOf( "#" ), -1 ) );
+ }
+
+ helpFound = true;
+ break;
+ }
+
+ return helpFound ? myUrl : helpNotFound;
+}
+
+
+QgsHelp::QgsHelp()
+{
+}
+
+QgsHelp::~QgsHelp()
+{
+}
+
+bool QgsHelp::urlExists( const QString& url ) const
+{
+ QUrl myUrl( url );
+ QTcpSocket socket;
+
+ socket.connectToHost( myUrl.host(), 80 );
+ if ( socket.waitForConnected() )
+ {
+ socket.write( "HEAD " + myUrl.path().toUtf8() + " HTTP/1.1\r\n"
+ "Host: " + myUrl.host().toUtf8() + "\r\n\r\n" );
+ if ( socket.waitForReadyRead() )
+ {
+ QByteArray bytes = socket.readAll();
+ if ( bytes.contains( "200 OK" ) || bytes.contains( "302 Found" ) )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/src/core/qgshelp.h b/src/core/qgshelp.h
new file mode 100644
index 00000000000..c71faa27bc4
--- /dev/null
+++ b/src/core/qgshelp.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ qgshelp.h
+ --------------------------------------
+ Date : December 2016
+ Copyright : (C) 2016 by Alexander Bruy
+ Email : alexander dot bruy at gmail dot com
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef QGSHELP_H
+#define QGSHELP_H
+
+#include
+
+/** \ingroup core
+ * @brief The class provides help URI for the given key.
+ *
+ * Help can be stored online, on the local directory or on the intranet
+ * server. Location of the QGIS help can be configured in QGIS options.
+ * Multiple locations are supported, they will be used in order of
+ * preference, from top to bottom.
+ *
+ * URI construction takes in account following information:
+ * - QGIS version
+ * - language of the QGIS UI
+ *
+ * If no help found, default error page with information how to setup
+ * help system will be shown.
+ *
+ * @note added in QGIS 3.0
+ */
+class CORE_EXPORT QgsHelp : public QObject
+{
+ Q_OBJECT
+ public:
+ /** Opens help topic for the given help key using default system
+ * web browser. If help topic not found, builtin error page shown.
+ * @param key key which identified help topic
+ * @note added in QGIS 3.0
+ */
+ static void openHelp( const QString& key );
+
+ /** Returns URI of the help topic for the given key. If help topic
+ * not found, URI of the builtin error page returned.
+ * @param key key which identified help topic
+ * @note added in QGIS 3.0
+ */
+ static QUrl helpUrl( const QString& key );
+
+ private:
+ //! Constructor
+ QgsHelp();
+
+ //! Destructor
+ ~QgsHelp();
+
+ /** Check if given URL accessible by issuing HTTP HEAD request.
+ * Returns true if URL accessible, false otherwise.
+ * @param url URL to check
+ * @note added in QGIS 3.0
+ */
+ bool urlExists( const QString& url ) const;
+
+ static QgsHelp* sHelp;
+};
+
+#endif // QGSHELP_H