Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/7031_deleting_scripts'
Browse files Browse the repository at this point in the history
Conflicts:
	Code/Mantid/MantidQt/API/inc/MantidQtAPI/ScriptRepositoryView.ui

Refs #7031
  • Loading branch information
Samuel Jackson committed Jul 5, 2013
2 parents 52b4bd0 + cb8e4a7 commit a7dfa65
Show file tree
Hide file tree
Showing 11 changed files with 743 additions and 50 deletions.
28 changes: 28 additions & 0 deletions Code/Mantid/Framework/API/inc/MantidAPI/ScriptRepository.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,34 @@ They will work as was expected for folders @ref folders-sec.
const std::string & author,
const std::string & email) = 0;

/**
Delete the file from the remote repository (it does not touch the local copy).
After this, the file will not be available for anyone among the users. As so,
it is required a justification of why to remove (the comment), and the current
rule accept that only the owner of the file (which is considered the last one
to use the file) is allowed to remove it.
The file will be removed from the central repository (git)
@note This operation requires internet connection.
@param file_path for the file to be deleted. It will not accept deleting folders for
security reason.
@param comment The reson of why deleting this entry.
@param author An string that may identify who is requesting to delete the file.
It accept only the last author to remove it.
@param email An string that identifies the email of the author.
@exception ScriptRepoException may be triggered for an attempt to delete folders,
or a non existent file, or not allowed operation, or any network erros.
*/
virtual void remove(const std::string & file_path, const std::string & comment,
const std::string & author,
const std::string & email) = 0;

/** Define the file patterns that will not be listed in listFiles.
This is important to force the ScriptRepository to not list hidden files,
automatic generated files and so on. This helps to present to the user a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ namespace API{
void upload(const std::string & file_path, const std::string & comment,
const std::string & author,
const std::string & email);
// remove file from the central repository and from local folder
void remove(const std::string & file_path, const std::string & comment,
const std::string & author,
const std::string & email);

/* Return true if there is a local repository installed*/
bool isValid(void);
Expand All @@ -117,8 +121,12 @@ namespace API{




virtual void doDownloadFile(const std::string & url_file, const std::string & local_file_path = "");
// convenient method to allow to perform the unit tests on remove files.
virtual std::string doDeleteRemoteFile(const std::string & url, const std::string & file_path,
const std::string & author, const std::string & email,
const std::string & comment);
protected:
void parseCentralRepository(Repository & repo);

Expand All @@ -128,7 +136,6 @@ namespace API{

void ensureValidRepository();


bool isEntryValid(const std::string & path);

/// Path of the local repository.
Expand Down
223 changes: 222 additions & 1 deletion Code/Mantid/Framework/ScriptRepository/src/ScriptRepositoryImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,9 @@ namespace API

// for the directories, update the status of this directory
if (entry.directory){
entry.status = acc_status;
entry.status = acc_status;
if (!entry.remote)
entry.status = Mantid::API::LOCAL_ONLY;
last_directory = entry_path;
}else{
// for the files, it evaluates the status of this file
Expand Down Expand Up @@ -846,6 +848,225 @@ namespace API
}
}

/**
* Delete one file from the local and the central ScriptRepository
* It will send in a POST method, with the file path to find the path :
* - author : Will identify the author of the change
* - email: Will identify the email of the author
* - comment: Description of the nature of the file or of the update
*
* It will them send the request to the URL pointed to UploaderWebServer, changing the word
* publish to remove. For example:
*
* http://upload.mantidproject.org/scriptrepository/payload/remove
*
* The server will them create a git commit deleting the file. And will reply with a json string
* with some usefull information about the success or failure of the attempt to delete.
* In failure, it will be converted to an appropriated ScriptRepoException.
*
* Requirements: in order to be allowed to delete files from the central repository,
* it is required that the state of the file must be BOTH_UNCHANGED or LOCAL_CHANGED.
*
* @param file_path: The path (relative to the repository) or absolute to identify the file to remove
* @param comment: justification to remove this file (will be used as git commit message)
* @param author: identification of the requester for deleting wich must be the author of the file as well
* @param email: email of the requester
*
* @exception ScriptRepoException justifying the reason to failure.
*
* @note only local files can be removed.
*/
void ScriptRepositoryImpl::remove(const std::string & file_path,
const std::string & comment,
const std::string & author,
const std::string & email){
std::string relative_path = convertPath(file_path);

// get the status, because only local files can be removed
SCRIPTSTATUS status = fileStatus(relative_path);
std::stringstream ss;
bool raise_exc = false;
switch(status){
case REMOTE_ONLY:
ss << "You are not allowed to remove files from the repository that you have not installed and you are not the owner";
raise_exc = true;
break;
case REMOTE_CHANGED:
case BOTH_CHANGED:
ss << "There is a new version of this file, so you can not remove it from the repository before checking it out. Please download the new version, and if you still wants to remove, do it afterwards";
raise_exc = true;
break;
case LOCAL_ONLY:
ss << "This operation is to remove files from the central repository. "
<< "\nTo delete files or folders from your local folder, please, do it through your operative system,"
<< "using your local installation folder at " << local_repository ;
raise_exc = true;
default:
break;
}
if (raise_exc)
throw ScriptRepoException(ss.str());

g_log.information() << "ScriptRepository deleting " << file_path << " ..." << std::endl;

{
// request to remove the file from the central repository

RepositoryEntry & entry = repo.at(relative_path);

if (entry.directory)
throw ScriptRepoException("You can not remove folders recursively from the central repository.");


// prepare the request, and call doDeleteRemoteFile to request the server to remove the file
std::string remote_delete = remote_upload;
boost::replace_all(remote_delete, "publish", "remove");
std::stringstream answer;
answer << doDeleteRemoteFile(remote_delete, file_path, author, email, comment);
g_log.debug() << "Answer from doDelete: " << answer.str() << std::endl;

// analyze the answer from the server, to see if the file was removed or not.
std::string info;
std::string detail;
std::string published_date;
ptree pt;
try{
read_json(answer, pt);
info = pt.get<std::string>("message","");
detail = pt.get<std::string>("detail","");
std::string cmd = pt.get<std::string>("shell","");
if (!cmd.empty())
detail.append("\nFrom Command: ").append(cmd);

}catch (boost::property_tree::json_parser_error & ex){
// this should not occurr in production. The answer from the
// server should always be a valid json file
g_log.debug() << "Bad answer: " << ex.what() << std::endl;
throw ScriptRepoException("Bad answer from the Server",
ex.what());
}

g_log.debug() << "Checking if success info=" << info << std::endl;
// check if the server removed the file from the central repository
if (info!= "success")
throw ScriptRepoException(info, detail); // no


g_log.notice() << "ScriptRepository " << file_path << " removed from central repository"<< std::endl;

// delete the entry from the repository.json. In reality, the repository.json should change at the
// remote repository, and we could only download the new one, but, practically, at the server, it will
// take sometime to be really removed, so, for practical reasons, this is dealt with locally.
//
{
ptree pt;
std::string filename = std::string(local_repository).append(".repository.json");
try{
read_json(filename,pt);
pt.erase(relative_path);// remove the entry
#if defined(_WIN32) || defined(_WIN64)
//set the .repository.json and .local.json not hidden (to be able to edit it)
SetFileAttributes( filename.c_str(), FILE_ATTRIBUTE_NORMAL);
#endif
write_json(filename,pt);
#if defined(_WIN32) || defined(_WIN64)
//set the .repository.json and .local.json hidden
SetFileAttributes( filename.c_str(), FILE_ATTRIBUTE_HIDDEN);
#endif
}catch (boost::property_tree::json_parser_error & ex){
std::stringstream ss;
ss << "corrupted central copy of database : " << filename;

g_log.error() << "ScriptRepository: " << ss.str()
<< "\nDetails: deleting entries - json_parser_error: " << ex.what() << std::endl;
throw ScriptRepoException(ss.str(), ex.what());
}
}

// update the repository list variable
// now, it is local_only and it is not inside remote.
// this is necessary for the strange case, where removing locally may fail.

entry.status = LOCAL_ONLY;
entry.remote = false;

}// file removed on central repository

}

/** Implements the request to the server to delete one file. It is created as a virtual protected member
* to allow creating unittest mocking the dependency on the internet connection. This method requires
* internet connection.
*
* @param url: url from the server that serves the request of removing entries
* @param file_path: relative path to the file inside the repository
* @param author: requester
* @param email: email from author
* @param comment: to be converted in git commit
* @return The answer from the server (json string)
*
* The server requires that the path, author, email and comment be given in order to create the commit
* for the git repository. Besides, it will ensure that the author and email are the same to the author
* and email for the last commit, in order not to allow deleting files that others are owner.
*
*/
std::string ScriptRepositoryImpl::doDeleteRemoteFile(const std::string & url, const std::string & file_path,
const std::string & author, const std::string & email,
const std::string & comment){
using namespace Poco::Net;
std::stringstream answer;
try{
// create the poco httprequest object
Poco::URI uri(url);
std::string path(uri.getPathAndQuery());
HTTPClientSession session(uri.getHost(), uri.getPort());
HTTPRequest req(HTTPRequest::HTTP_POST, path,
HTTPMessage::HTTP_1_0);
g_log.debug() << "Receive request to delete file " << file_path << " using " << url << std::endl;


// fill up the form required from the server to delete one file, with the fields
// path, author, comment, email
HTMLForm form;
form.add("author",author);
form.add("mail", email);
form.add("comment",comment);
form.add("file_n",file_path);

// send the request to the server
form.prepareSubmit(req);
std::ostream& ostr = session.sendRequest(req);
form.write(ostr);

// get the answer from the server
HTTPResponse response;
std::istream & rs = session.receiveResponse(response);

g_log.debug() << "ScriptRepository delete status: "
<< response.getStatus() << " " << response.getReason() << std::endl;

{
// get the answer from the server
std::stringstream server_reply;
std::string server_reply_str;
Poco::StreamCopier::copyStream(rs, server_reply);
server_reply_str= server_reply.str();
// remove the status message from the end of the reply,
// in order not to get exception from the read_json parser
size_t pos = server_reply_str.rfind("}");
if (pos != std::string::npos)
answer << std::string(server_reply_str.begin() , server_reply_str.begin() + pos + 1);
else
answer << server_reply_str;
}
g_log.debug() << "Form Output: " << answer.str() << std::endl;
}catch(Poco::Exception & ex){
throw ScriptRepoException(ex.displayText(), ex.className());
}
return answer.str();
}


/** The ScriptRepositoryImpl is set to be valid when the local repository path
points to a valid folder that has also the .repository.json and .local.json files.
Expand Down

0 comments on commit a7dfa65

Please sign in to comment.