1 /***************************************************************************
2 qgsaction.cpp - QgsAction
3
4 ---------------------
5 begin : 18.4.2016
6 copyright : (C) 2016 by Matthias Kuhn
7 email : matthias@opengis.ch
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgsaction.h"
18
19 #include <QDesktopServices>
20 #include <QFileInfo>
21 #include <QUrl>
22
23 #include "qgspythonrunner.h"
24 #include "qgsrunprocess.h"
25 #include "qgsexpressioncontext.h"
26 #include "qgsvectorlayer.h"
27 #include "qgslogger.h"
28 #include "qgsexpressioncontextutils.h"
29
runable() const30 bool QgsAction::runable() const
31 {
32 return mType == Generic ||
33 mType == GenericPython ||
34 mType == OpenUrl ||
35 #if defined(Q_OS_WIN)
36 mType == Windows
37 #elif defined(Q_OS_MAC)
38 mType == Mac
39 #else
40 mType == Unix
41 #endif
42 ;
43 }
44
run(QgsVectorLayer * layer,const QgsFeature & feature,const QgsExpressionContext & expressionContext) const45 void QgsAction::run( QgsVectorLayer *layer, const QgsFeature &feature, const QgsExpressionContext &expressionContext ) const
46 {
47 QgsExpressionContext actionContext( expressionContext );
48
49 actionContext << QgsExpressionContextUtils::layerScope( layer );
50 actionContext.setFeature( feature );
51
52 run( actionContext );
53 }
54
run(const QgsExpressionContext & expressionContext) const55 void QgsAction::run( const QgsExpressionContext &expressionContext ) const
56 {
57 if ( !isValid() )
58 {
59 QgsDebugMsg( QStringLiteral( "Invalid action cannot be run" ) );
60 return;
61 }
62
63 QgsExpressionContextScope *scope = new QgsExpressionContextScope( mExpressionContextScope );
64 QgsExpressionContext context( expressionContext );
65 context << scope;
66
67 const QString expandedAction = QgsExpression::replaceExpressionText( mCommand, &context );
68
69 if ( mType == QgsAction::OpenUrl )
70 {
71 const QFileInfo finfo( expandedAction );
72 if ( finfo.exists() && finfo.isFile() )
73 QDesktopServices::openUrl( QUrl::fromLocalFile( expandedAction ) );
74 else
75 QDesktopServices::openUrl( QUrl( expandedAction, QUrl::TolerantMode ) );
76 }
77 else if ( mType == QgsAction::GenericPython )
78 {
79 // TODO: capture output from QgsPythonRunner (like QgsRunProcess does)
80 QgsPythonRunner::run( expandedAction );
81 }
82 else
83 {
84 // The QgsRunProcess instance created by this static function
85 // deletes itself when no longer needed.
86 QgsRunProcess::create( expandedAction, mCaptureOutput );
87 }
88 }
89
actionScopes() const90 QSet<QString> QgsAction::actionScopes() const
91 {
92 return mActionScopes;
93 }
94
setActionScopes(const QSet<QString> & actionScopes)95 void QgsAction::setActionScopes( const QSet<QString> &actionScopes )
96 {
97 mActionScopes = actionScopes;
98 }
99
readXml(const QDomNode & actionNode)100 void QgsAction::readXml( const QDomNode &actionNode )
101 {
102 QDomElement actionElement = actionNode.toElement();
103 const QDomNodeList actionScopeNodes = actionElement.elementsByTagName( QStringLiteral( "actionScope" ) );
104
105 if ( actionScopeNodes.isEmpty() )
106 {
107 mActionScopes
108 << QStringLiteral( "Canvas" )
109 << QStringLiteral( "Field" )
110 << QStringLiteral( "Feature" );
111 }
112 else
113 {
114 for ( int j = 0; j < actionScopeNodes.length(); ++j )
115 {
116 const QDomElement actionScopeElem = actionScopeNodes.item( j ).toElement();
117 mActionScopes << actionScopeElem.attribute( QStringLiteral( "id" ) );
118 }
119 }
120
121 mType = static_cast< QgsAction::ActionType >( actionElement.attributeNode( QStringLiteral( "type" ) ).value().toInt() );
122 mDescription = actionElement.attributeNode( QStringLiteral( "name" ) ).value();
123 mCommand = actionElement.attributeNode( QStringLiteral( "action" ) ).value();
124 mIcon = actionElement.attributeNode( QStringLiteral( "icon" ) ).value();
125 mCaptureOutput = actionElement.attributeNode( QStringLiteral( "capture" ) ).value().toInt() != 0;
126 mShortTitle = actionElement.attributeNode( QStringLiteral( "shortTitle" ) ).value();
127 mNotificationMessage = actionElement.attributeNode( QStringLiteral( "notificationMessage" ) ).value();
128 mIsEnabledOnlyWhenEditable = actionElement.attributeNode( QStringLiteral( "isEnabledOnlyWhenEditable" ) ).value().toInt() != 0;
129 mId = QUuid( actionElement.attributeNode( QStringLiteral( "id" ) ).value() );
130 if ( mId.isNull() )
131 mId = QUuid::createUuid();
132 }
133
writeXml(QDomNode & actionsNode) const134 void QgsAction::writeXml( QDomNode &actionsNode ) const
135 {
136 QDomElement actionSetting = actionsNode.ownerDocument().createElement( QStringLiteral( "actionsetting" ) );
137 actionSetting.setAttribute( QStringLiteral( "type" ), mType );
138 actionSetting.setAttribute( QStringLiteral( "name" ), mDescription );
139 actionSetting.setAttribute( QStringLiteral( "shortTitle" ), mShortTitle );
140 actionSetting.setAttribute( QStringLiteral( "icon" ), mIcon );
141 actionSetting.setAttribute( QStringLiteral( "action" ), mCommand );
142 actionSetting.setAttribute( QStringLiteral( "capture" ), mCaptureOutput );
143 actionSetting.setAttribute( QStringLiteral( "notificationMessage" ), mNotificationMessage );
144 actionSetting.setAttribute( QStringLiteral( "isEnabledOnlyWhenEditable" ), mIsEnabledOnlyWhenEditable );
145 actionSetting.setAttribute( QStringLiteral( "id" ), mId.toString() );
146
147 const auto constMActionScopes = mActionScopes;
148 for ( const QString &scope : constMActionScopes )
149 {
150 QDomElement actionScopeElem = actionsNode.ownerDocument().createElement( QStringLiteral( "actionScope" ) );
151 actionScopeElem.setAttribute( QStringLiteral( "id" ), scope );
152 actionSetting.appendChild( actionScopeElem );
153 }
154
155 actionsNode.appendChild( actionSetting );
156 }
157
setExpressionContextScope(const QgsExpressionContextScope & scope)158 void QgsAction::setExpressionContextScope( const QgsExpressionContextScope &scope )
159 {
160 mExpressionContextScope = scope;
161 }
162
expressionContextScope() const163 QgsExpressionContextScope QgsAction::expressionContextScope() const
164 {
165 return mExpressionContextScope;
166 }
167
html() const168 QString QgsAction::html() const
169 {
170 QString typeText;
171 switch ( mType )
172 {
173 case Generic:
174 {
175 typeText = QObject::tr( "Generic" );
176 break;
177 }
178 case GenericPython:
179 {
180 typeText = QObject::tr( "Generic Python" );
181 break;
182 }
183 case Mac:
184 {
185 typeText = QObject::tr( "Mac" );
186 break;
187 }
188 case Windows:
189 {
190 typeText = QObject::tr( "Windows" );
191 break;
192 }
193 case Unix:
194 {
195 typeText = QObject::tr( "Unix" );
196 break;
197 }
198 case OpenUrl:
199 {
200 typeText = QObject::tr( "Open URL" );
201 break;
202 }
203 }
204 return { QObject::tr( R"html(
205 <h2>Action Details</h2>
206 <p>
207 <b>Description:</b> %1<br>
208 <b>Short title:</b> %2<br>
209 <b>Type:</b> %3<br>
210 <b>Scope:</b> %4<br>
211 <b>Action:</b><br>
212 <pre>%6</pre>
213 </p>
214 )html" ).arg( mDescription, mShortTitle, typeText, actionScopes().values().join( QLatin1String( ", " ) ), mCommand )};
215 };
216