Gary Spencer c8ed7691e0 Added environment variable expansion to the command line handling of desktop actions, plus the special '%AlfrescoDir%' token for the
current working directory path (from the client side).
Updated sample commandline test action to use '%SystemRoot%\Notepad.exe'.
Added lock check to check out action code to give more meaningful error message.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3689 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2006-09-05 10:04:44 +00:00

718 lines
18 KiB
C++

/*
* Copyright (C) 2005-2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
#include "stdafx.h"
#include "CAlfrescoApp.h"
#include "CAlfrescoAppDlg.h"
#include <stdlib.h>
#include "util\String.h"
#include "util\DataBuffer.h"
#include "util\FileName.h"
#include "util\Integer.h"
#include <shellapi.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
using namespace std;
using namespace Alfresco;
// CCAlfrescoAppApp
BEGIN_MESSAGE_MAP(CAlfrescoApp, CWinApp)
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
// CCAlfrescoApp construction
CAlfrescoApp::CAlfrescoApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
// The one and only CCAlfrescoAppApp object
CAlfrescoApp theApp;
// CCAlfrescoAppApp initialization
BOOL CAlfrescoApp::InitInstance()
{
// InitCommonControls() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
InitCommonControls();
CWinApp::InitInstance();
AfxEnableControlContainer();
// Get the application path
String appPath = __wargv[0];
int pos = appPath.lastIndexOf(PathSeperator);
if ( pos < 0) {
AfxMessageBox( L"Invalid application path", MB_OK | MB_ICONSTOP);
return 1;
}
// Get the path to the folder containing the application
String folderPath = appPath.substring(0, pos);
String exeName = appPath.substring(pos + 1);
// Create the Alfresco interface
AlfrescoInterface alfresco(folderPath);
if ( alfresco.isAlfrescoFolder()) {
try {
// Get the action information
AlfrescoActionInfo actionInfo = alfresco.getActionInformation(exeName);
// Check if the action should be confirmed
if ( actionInfo.hasPreProcessAction(PreConfirmAction)) {
// Get the confirmation message
String confirmMsg = actionInfo.getConfirmationMessage();
if ( confirmMsg.length() == 0)
confirmMsg = L"Run action ?";
// Display a confirmation dialog
if ( AfxMessageBox( confirmMsg, MB_OKCANCEL | MB_ICONQUESTION) == IDCANCEL)
return FALSE;
}
// Check if the action supports multiple paths, if not then call the action once for each supplied path
if ( actionInfo.hasAttribute(AttrMultiplePaths)) {
// Build a list of paths from the command line arguments
StringList pathList;
for ( int i = 1; i < __argc; i++)
pathList.addString( String(__wargv[i]));
// Run the action
runAction( alfresco, pathList, actionInfo);
}
// Check if the action supports file/folder targets
else if ( actionInfo.hasAttribute( AttrAnyFilesFolders) == true) {
// Pass one path at a time to the action
for ( int i = 1; i < __argc; i++) {
// Create a path list with a single path
StringList pathList;
pathList.addString( String(__wargv[i]));
// Run the action
runAction( alfresco, pathList, actionInfo);
}
}
// Action does not use targets, just run the action
else if ( actionInfo.allowsNoParameters()) {
// Run the action
StringList emptyList;
runAction( alfresco, emptyList, actionInfo);
}
}
catch (Exception ex) {
CString msg;
msg.FormatMessage( L"Exception occurred\n\n%1", ex.getMessage().data());
AfxMessageBox( msg, MB_OK | MB_ICONSTOP);
}
}
else {
AfxMessageBox( L"Not a valid Alfresco CIFS folder", MB_OK | MB_ICONSTOP);
return 1;
}
// Exit the application
return FALSE;
}
/**
* Process the command line arguments and build the parameter list for the desktop action
*
* @param alfresco AlfrescoInterface&
* @param paths StringList&
* @param actionInfo AlfrescoActionInfo&
* @param params DesktopParams&
* @return bool
*/
bool CAlfrescoApp::buildDesktopParameters( AlfrescoInterface& alfresco, StringList& paths, AlfrescoActionInfo& actionInfo,
DesktopParams& params) {
// If there are no paths then just return a success
if ( paths.numberOfStrings() == 0)
return true;
// Process the list of files and either check in the file if it is a working copy or check out
// the file
for ( unsigned int i = 0; i < paths.numberOfStrings(); i++) {
// Get the current file name
String curFile = paths.getStringAt( i);
// Check if the path is on an Alfresco mapped drive
if ( alfresco.isMappedDrive() && curFile.startsWithIgnoreCase( alfresco.getDrivePath())) {
// Convert the path to a UNC path
String uncPath = alfresco.getRootPath();
uncPath.append( curFile.substring(2));
curFile = uncPath;
}
// Check if the path is to a file/folder, and whether it is a local path
bool copyFile = false;
DWORD attr = GetFileAttributes( curFile);
if ( attr != INVALID_FILE_ATTRIBUTES) {
// Check if the action supports the file/folder type
bool isDir = (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 ? true : false;
if ( isDir && actionInfo.supportsFolders() == false) {
AfxMessageBox(L"Action does not support folders", MB_OK | MB_ICONSTOP);
return false;
}
else if ( actionInfo.supportsFiles() == false) {
AfxMessageBox(L"Action does not support files", MB_OK | MB_ICONSTOP);
return false;
}
// Get the file name from the path
StringList nameParts = FileName::splitPath( curFile);
String curName = nameParts.getStringAt( 1);
// If the path is to a file that is not on the Alfresco share the file will need to be copied,
// after checking the status of a matching file in the Alfresco folder
if ( curFile.length() >= 3 && curFile.substring(1,3).equals( L":\\")) {
// Check if the action supports local files
if ( isDir == false && actionInfo.hasAttribute(AttrClientFiles) == false) {
AfxMessageBox(L"Action does not support local files", MB_OK | MB_ICONSTOP);
return false;
}
else if ( isDir == true && actionInfo.hasAttribute(AttrClientFolders) == false) {
AfxMessageBox(L"Action does not support local folders", MB_OK | MB_ICONSTOP);
return false;
}
// Check if there is an existing file in the Alfresco with the same name, check if the file is locked
PTR_AlfrescoFileInfo fInfo = alfresco.getFileInformation( curName);
if ( fInfo.get() != NULL) {
// There is an existing file in the Alfresco folder with the same name, check if it is locked
if ( fInfo->getLockType() != LockNone) {
AfxMessageBox( L"Cannot copy file to Alfresco folder, destination file is locked", MB_OK | MB_ICONEXCLAMATION);
return false;
}
else if ( actionInfo.hasPreProcessAction(PreLocalToWorkingCopy) == true && fInfo->isWorkingCopy() == false) {
AfxMessageBox( L"Cannot copy to Alfresco folder, destination must overwrite a working copy", MB_OK | MB_ICONEXCLAMATION);
return false;
}
}
else if ( actionInfo.hasPreProcessAction(PreLocalToWorkingCopy) == true) {
// Target folder does not contain a matching working copy of the local file
CString msg;
msg.FormatMessage( L"No matching working copy for %1", curName.data());
AfxMessageBox( msg, MB_OK | MB_ICONEXCLAMATION);
return false;
}
// Copy the files/folders using the Windows shell
bool copyAborted = false;
if ( copyFilesUsingShell( curFile, alfresco.getUNCPath(), copyAborted) == false) {
// Check if the copy failed or the user aborted the copy
if ( copyAborted == false) {
// File copy failed
CString msg;
msg.FormatMessage( isDir ? L"Failed to copy folder %1" : L"Failed to copy file %1", curFile.data());
AfxMessageBox( msg, MB_OK | MB_ICONSTOP);
return false;
}
else {
// User aborted the file copy
CString msg;
msg.FormatMessage( L"Copy aborted for %1", curFile.data());
AfxMessageBox( msg, MB_OK | MB_ICONSTOP);
return false;
}
}
// Add a desktop target for the copied file
params.addTarget( new DesktopTarget(isDir ? TargetCopiedFolder : TargetCopiedFile, curName));
}
else {
// Path is a UNC path, check if the file/folder is in the same folder as the action
DesktopTarget* pTarget = NULL;
if ( curFile.startsWith( alfresco.getUNCPath())) {
// Path is in the same folder as the application, or in a sub-folder
String relPath = curFile.substring( alfresco.getUNCPath().length() + 1);
if ( relPath.indexOf( L"\\") == -1) {
// Create a target using the file name only
pTarget = new DesktopTarget( isDir ? TargetFolder : TargetFile, relPath);
}
}
// If the target is not valid the file/folder is not in the same folder as the client-side application,
// copy the files/folders to the target folder or use the root relative path to the file/folder
if ( pTarget == NULL) {
// Check if Alfresco files/folders should be copied to the target folder
if ( actionInfo.hasPreProcessAction(PreCopyToTarget)) {
// Copy the files/folders using the Windows shell
bool copyAborted = false;
if ( copyFilesUsingShell( curFile, alfresco.getUNCPath(), copyAborted) == false) {
// Check if the copy failed or the user aborted the copy
if ( copyAborted == false) {
// File copy failed
CString msg;
msg.FormatMessage( isDir ? L"Failed to copy folder %1" : L"Failed to copy file %1", curFile.data());
AfxMessageBox( msg, MB_OK | MB_ICONSTOP);
return false;
}
else {
// User aborted the file copy
CString msg;
msg.FormatMessage( L"Copy aborted for %1", curFile.data());
AfxMessageBox( msg, MB_OK | MB_ICONSTOP);
return false;
}
}
// Add a desktop target for the copied file
pTarget= new DesktopTarget(isDir ? TargetCopiedFolder : TargetCopiedFile, curName);
}
else {
// Get the root relative path to the file/folder
String rootRelPath = curFile.substring(alfresco.getRootPath().length());
pTarget = new DesktopTarget( isDir ? TargetFolder : TargetFile, rootRelPath);
}
}
// Add the desktop target
params.addTarget( pTarget);
}
}
}
// Return status
return true;
}
/**
* Copy a file/folder using the Windows shell
*
* @param fromFileFolder const String&
* @param toFolder const String&
* @param aborted bool&
* @return bool
*/
bool CAlfrescoApp::copyFilesUsingShell(const String& fromFileFolder, const String& toFolder, bool& aborted) {
// Build the from/to paths, must be double null terminated
wchar_t fromPath[MAX_PATH + 1];
wchar_t toPath[MAX_PATH + 1];
memset( fromPath, 0, sizeof( fromPath));
memset( toPath, 0, sizeof( toPath));
wcscpy( fromPath, fromFileFolder.data());
wcscpy( toPath, toFolder.data());
// Copy the local file to the Alfresco folder
SHFILEOPSTRUCT fileOpStruct;
memset( &fileOpStruct, 0, sizeof(SHFILEOPSTRUCT));
fileOpStruct.hwnd = HWND_DESKTOP;
fileOpStruct.wFunc = FO_COPY;
fileOpStruct.pFrom = fromPath;
fileOpStruct.pTo = toPath;
fileOpStruct.fFlags= 0;
fileOpStruct.fAnyOperationsAborted =false;
// Copy the file to the Alfresco folder
bool sts = false;
if ( SHFileOperation( &fileOpStruct) == 0) {
// File copy successful
sts = true;
}
else if ( fileOpStruct.fAnyOperationsAborted) {
// User aborted the file copy
aborted = true;
}
// Return the copy status
return sts;
}
/**
* Run an action
*
* @param alfresco AlfrescoInterface&
* @param pathList StringList&
* @param actionInfo AlfrescoActionInfo&
* @return bool
*/
bool CAlfrescoApp::runAction( AlfrescoInterface& alfresco, StringList& pathList, AlfrescoActionInfo& actionInfo) {
// Build the desktop action parameter list, perform any file copying of local files
bool sts = false;
DesktopParams desktopParams;
if ( buildDesktopParameters( alfresco, pathList, actionInfo, desktopParams)) {
// Check if the action requires parameters
if ( actionInfo.allowsNoParameters() == false && desktopParams.numberOfTargets() == 0) {
AfxMessageBox( L"No parameters for action", MB_OK | MB_ICONEXCLAMATION);
return false;
}
// Run the desktop action
DesktopResponse response = alfresco.runAction( actionInfo, desktopParams);
// Check the response status
if ( response.getStatus() != StsSuccess) {
// Check if the status indicates a command line should be launched
if ( response.getStatus() == StsCommandLine) {
// Launch a process using the command line
sts = doCommandLine( alfresco, response.getStatusMessage());
}
// Check if a web browser should be launched with a URL
else if ( response.getStatus() == StsLaunchURL) {
// Use the Windows shell to open the URL
sts = doURL( alfresco, response.getStatusMessage());
}
// Error status
else {
// Get the error message
String errMsg;
switch ( response.getStatus()) {
case StsFileNotFound:
errMsg = L"File not found";
break;
case StsAccessDenied:
errMsg = L"Access denied";
break;
case StsBadParameter:
errMsg = L"Bad parameter in request";
break;
case StsNoSuchAction:
errMsg = L"No such action";
break;
default:
errMsg = L"Error running action";
break;
}
// Display an error dialog
CString msg;
if ( response.hasStatusMessage())
msg.FormatMessage( L"%1\n\n%2", errMsg.data(), response.getStatusMessage().data());
else
msg = errMsg.data();
AfxMessageBox( msg, MB_OK | MB_ICONERROR);
}
}
else if ( response.hasStatusMessage()) {
// Display the message returned by the action
CString msg;
msg.FormatMessage( L"Action returned message\n\n%1", response.getStatusMessage().data());
AfxMessageBox( msg, MB_OK | MB_ICONINFORMATION);
}
}
// Return the action status
return sts;
}
/**
* Launch a command line
*
* @param alfresco AlfrescoInterface&
* @param cmdStr const String&
* @return bool
*/
bool CAlfrescoApp::doCommandLine( AlfrescoInterface& alfresco, const String& cmdStr) {
// Check if the command line contains any environment variables/tokens
String cmdLine = cmdStr;
int pos = cmdLine.indexOf( L'%');
if ( pos != -1) {
// Command line contains environment variables or other tokens that must be replaced
String newCmdLine = L"";
if (pos > 0)
newCmdLine = cmdLine.substring( 0, pos);
wchar_t envBuf[256];
size_t envLen;
while ( pos != -1) {
// Find the end of the current token
int endPos = cmdLine.indexOf ( L'%', pos + 1);
if ( endPos == -1) {
CString msg;
msg.FormatMessage( L"Bad token in command line\n\n%1", cmdLine.data());
AfxMessageBox( msg, MB_OK | MB_ICONERROR);
return false;
}
// Extract the token
String token = cmdLine.substring( pos + 1, endPos);
// Replace the token with an environment variable value or other values
if ( token.equals( L"AlfrescoDir")) {
// Use the local path to the Alfresco folder that the application is running from
newCmdLine.append( alfresco.getUNCPath());
}
else {
// Find the environment variable value
envLen = sizeof( envBuf)/sizeof(wchar_t);
const wchar_t* pEnvName = token.data();
if ( _wgetenv_s( &envLen, envBuf, envLen, pEnvName) == 0) {
// Append the environment variable value
newCmdLine.append( envBuf);
}
else {
// Error converting the environment variable
CString msg;
msg.FormatMessage( L"Failed to convert environment variable\n\n%1\n\n%2", token.data(), cmdLine.data());
AfxMessageBox( msg, MB_OK | MB_ICONERROR);
return false;
}
}
// Update the token search position
pos = endPos + 1;
if (( unsigned int) pos < cmdStr.length()) {
// Search for the next token
pos = cmdLine.indexOf( L'%', pos);
}
else {
// End of string, finish the token search
pos = -1;
}
// Append the normal string between tokens
if ( pos > (endPos + 1)) {
// Get the between token sting
String filler = cmdLine.substring( endPos + 1, pos);
newCmdLine.append( filler);
}
else if ( pos == -1) {
// Append the remaining string
String filler = cmdLine.substring( endPos + 1);
newCmdLine.append( filler);
}
}
// Update the command line
cmdLine = newCmdLine;
}
// Initialize the startup information
STARTUPINFO startupInfo;
memset(&startupInfo, 0, sizeof(STARTUPINFO));
// Launch a process using the command line
PROCESS_INFORMATION processInfo;
memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
bool sts = false;
if ( CreateProcess( NULL, (LPWSTR) cmdLine.data(), NULL, NULL, true, 0, NULL, NULL,
&startupInfo, &processInfo) == false) {
CString msg;
msg.FormatMessage( L"Failed to launch command line\n\n%1\n\nError %2!d!", cmdLine.data(), GetLastError());
AfxMessageBox( msg, MB_OK | MB_ICONERROR);
}
else
sts = true;
return sts;
}
/**
* Browse to a URL
*
* @param alfresco AlfrescoInterface&
* @param url const String&
* @return bool
*/
bool CAlfrescoApp::doURL( AlfrescoInterface& alfresco, const String& url) {
// Use the Windows shell to open the URL
bool sts = false;
HINSTANCE shellSts = ShellExecute( NULL, NULL, url.data(), NULL, NULL, SW_SHOWNORMAL);
if (( int) shellSts < 32) {
CString msg;
msg.FormatMessage( L"Failed to launch URL\n\n%1", url.data());
AfxMessageBox( msg, MB_OK | MB_ICONERROR);
}
else
sts = true;
return sts;
}