Skip to content

Commit

Permalink
Merge branch 'pluginmanager_votes'
Browse files Browse the repository at this point in the history
  • Loading branch information
borysiasty committed Apr 1, 2014
2 parents 9eb4bb6 + 3b198d9 commit d398560
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 80 deletions.
15 changes: 15 additions & 0 deletions python/pyplugin_installer/installer.py
Expand Up @@ -188,6 +188,7 @@ def exportPluginsToManager(self):
plugin = plugins.all()[key]
iface.pluginManagerInterface().addPluginMetadata({
"id" : key,
"plugin_id" : plugin["plugin_id"] or "",
"name" : plugin["name"],
"description" : plugin["description"],
"about" : plugin["about"],
Expand Down Expand Up @@ -512,3 +513,17 @@ def setRepositoryInspectionFilter(self, reposName = None):
reposName = reposName.decode("utf-8")
repositories.setInspectionFilter(reposName)
self.reloadAndExportData()


# ----------------------------------------- #
def sendVote(self, plugin_id, vote):
""" send vote via the RPC """

if not plugin_id or not vote:
return False
url = "http://plugins.qgis.org/plugins/RPC2/"
params = "{\"id\":\"djangorpc\",\"method\":\"plugin.vote\",\"params\":[%s,%s]}" % (str(plugin_id), str(vote))
req = QNetworkRequest(QUrl(url))
req.setRawHeader("Content-Type", "application/json");
reply = QgsNetworkAccessManager.instance().post(req, params)
return True
9 changes: 8 additions & 1 deletion python/pyplugin_installer/installer_data.py
Expand Up @@ -413,8 +413,14 @@ def xmlDownloaded(self):
if icon and not icon.startswith("http"):
icon = "http://%s/%s" % ( QUrl(self.mRepositories[reposName]["url"]).host() , icon )

if pluginNodes.item(i).toElement().hasAttribute("plugin_id"):
plugin_id = pluginNodes.item(i).toElement().attribute("plugin_id")
else:
plugin_id = None

plugin = {
"id" : name,
"plugin_id" : plugin_id,
"name" : pluginNodes.item(i).toElement().attribute("name"),
"version_available" : pluginNodes.item(i).toElement().attribute("version"),
"description" : pluginNodes.item(i).firstChildElement("description").text().strip(),
Expand Down Expand Up @@ -647,6 +653,7 @@ def pluginMetadata(fct):

plugin = {
"id" : key,
"plugin_id" : None,
"name" : pluginMetadata("name") or key,
"description" : pluginMetadata("description"),
"about" : pluginMetadata("about"),
Expand Down Expand Up @@ -749,7 +756,7 @@ def rebuild(self):
if not self.mPlugins[key][attrib] and plugin[attrib]:
self.mPlugins[key][attrib] = plugin[attrib]
# other remote metadata is preffered:
for attrib in ["name", "description", "about", "category", "tags", "changelog", "author_name", "author_email", "homepage",
for attrib in ["name", "plugin_id", "description", "about", "category", "tags", "changelog", "author_name", "author_email", "homepage",
"tracker", "code_repository", "experimental", "deprecated", "version_available", "zip_repository",
"download_url", "filename", "downloads", "average_vote", "rating_votes"]:
if ( not attrib in translatableAttributes ) or ( attrib == "name" ): # include name!
Expand Down
1 change: 1 addition & 0 deletions src/app/pluginmanager/README
@@ -1,6 +1,7 @@
PLUGIN METADATA TAGS
=======================================================
id the key; C++ library base name or Python module name
plugin_id for the official repository: an integer id. At the time, used for voting only.
name human readable plugin name
description short description of the plugin purpose only
about longer description: how does it work, where does it install, how to run it?
Expand Down
196 changes: 147 additions & 49 deletions src/app/pluginmanager/qgspluginmanager.cpp
Expand Up @@ -30,6 +30,8 @@
#include <QActionGroup>
#include <QTextStream>
#include <QTimer>
#include <QWebPage>
#include <QDesktopServices>

#include "qgis.h"
#include "qgisapp.h"
Expand Down Expand Up @@ -95,6 +97,7 @@ QgsPluginManager::QgsPluginManager( QWidget * parent, bool pluginsAreEnabled, Qt

// Preset widgets
leFilter->setFocus( Qt::MouseFocusReason );
wvDetails->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);

// Don't restore the last used tab from QSettings
mOptionsListWidget->setCurrentRow( 0 );
Expand All @@ -103,7 +106,6 @@ QgsPluginManager::QgsPluginManager( QWidget * parent, bool pluginsAreEnabled, Qt
connect( mOptionsListWidget, SIGNAL( currentRowChanged( int ) ), this, SLOT( setCurrentTab( int ) ) );
connect( vwPlugins->selectionModel(), SIGNAL( currentChanged( const QModelIndex &, const QModelIndex & ) ), this, SLOT( currentPluginChanged( const QModelIndex & ) ) );
connect( mModelPlugins, SIGNAL( itemChanged( QStandardItem * ) ), this, SLOT( pluginItemChanged( QStandardItem * ) ) );

// Force setting the status filter (if the active tab was 0, the setCurrentRow( 0 ) above doesn't take any action)
setCurrentTab( 0 );

Expand Down Expand Up @@ -471,7 +473,7 @@ void QgsPluginManager::reloadModelData()

if ( !mCurrentlyDisplayedPlugin.isEmpty() )
{
tbDetails->setHtml( "" );
wvDetails->setHtml( "" );
buttonInstall->setEnabled( false );
buttonUninstall->setEnabled( false );
}
Expand Down Expand Up @@ -599,23 +601,79 @@ void QgsPluginManager::showPluginDetails( QStandardItem * item )
if ( ! metadata ) return;

QString html = "";

// // A future mockup for install/uninstall html controls
// "<table bgcolor=\"#CCCCCC\" cellspacing=\"5\" cellpadding=\"10\" width=\"100%\" height=\"100px\">"
// "<tr><td colspan=\"3\"><b>This plugin is installed</b></td></tr>"
// "<tr>"
// " <td></td>"
// " <td bgcolor=\"#AAFFAA\" align=\"right\"><a href=\"foo\" style=\"text-decoration:none;\" ><b>REINSTALL</b></a></td>"
// " <td bgcolor=\"#FFFFAA\" align=\"right\"><a href=\"foo\" style=\"text-decoration:none;\" ><b>UNINSTALL</b></a></td>"
// "</tr>"
// "</table><br/>";
// // -----------------------------------------
// // Print all tags for debug purposes
// QList<QString> keys = metadata->keys();
// for ( int i=0; i < keys.size(); ++i )
// {
// html += QString( "%1: %2 <br/>" ).arg( keys.at( i ) ).arg( metadata->value( keys.at( i ) ) );
// }
html += "<style>"
" body, table {"
" padding:0px;"
" margin:0px;"
" font-family:verdana;"
" font-size: 12px;"
" }"
" div#votes {"
" width:360px;"
" margin-left:98px;"
" padding-top:3px;"
" }"
"</style>";

if ( ! metadata->value( "plugin_id" ).isEmpty() )
{
html += QString(
"<style>"
" div#stars_bg {"
" background-image: url('file:///home/borys/Pobrane/stars_empty.png');"
" width:92px;"
" height:16px;"
" }"
" div#stars {"
" background-image: url('file:///home/borys/Pobrane/stars_full.png');"
" width:%1px;"
" height:16px;"
" }"
"</style>").arg( metadata->value( "average_vote" ).toFloat() / 5 * 92 );
html += QString(
"<script>"
" var plugin_id=%1;"
" var vote=0;"
" function ready()"
" {"
" document.getElementById('stars_bg').onmouseover=save_vote;"
" document.getElementById('stars_bg').onmouseout=restore_vote;"
" document.getElementById('stars_bg').onmousemove=change_vote;"
" document.getElementById('stars_bg').onclick=send_vote;"
" };"
" "
" function save_vote(e)"
" {"
" vote = document.getElementById('stars').style.width"
" }"
" "
" function restore_vote(e)"
" {"
" document.getElementById('stars').style.width = vote;"
" }"
" "
" function change_vote(e)"
" {"
" var length = e.x - document.getElementById('stars').getBoundingClientRect().left;"
" max = document.getElementById('stars_bg').getBoundingClientRect().right;"
" if ( length <= max ) document.getElementById('stars').style.width = length + 'px';"
" }"
" "
" function send_vote(e)"
" {"
" save_vote();"
" result = Number(vote.replace('px',''));"
" if (!result) return;"
" result = Math.floor(result/92*5)+1;"
" document.getElementById('send_vote_trigger').href='rpc2://plugin.vote/'+plugin_id+'/'+result;"
" ev=document.createEvent('MouseEvents');"
" ev.initEvent('click', false, true);"
" document.getElementById('send_vote_trigger').dispatchEvent(ev);"
" }"
"</script>").arg( metadata->value( "plugin_id" ) );
}

html += "<body onload='ready()'>";

// First prepare message box(es)
if ( ! metadata->value( "error" ).isEmpty() )
Expand Down Expand Up @@ -659,62 +717,66 @@ void QgsPluginManager::showPluginDetails( QStandardItem * item )
{
html += QString( "<table bgcolor=\"#EEEEBB\" cellspacing=\"2\" cellpadding=\"2\" width=\"100%\">"
" <tr><td width=\"100%\" style=\"color:#660000\">"
" <img src=\":/images/themes/default/pluginExperimental.png\" width=\"32\"><b>%1</b>"
" <img src=\"qrc:/images/themes/default/pluginExperimental.png\" width=\"32\"><b>%1</b>"
" </td></tr>"
"</table>" ).arg( tr( "This plugin is experimental" ) );
};
if ( metadata->value( "deprecated" ) == "true" )
{
html += QString( "<table bgcolor=\"#EEBBCC\" cellspacing=\"2\" cellpadding=\"2\" width=\"100%\">"
" <tr><td width=\"100%\" style=\"color:#660000\">"
" <img src=\":/images/themes/default/pluginDeprecated.png\" width=\"32\"><b>%1</b>"
" <img src=\"qrc:/images/themes/default/pluginDeprecated.png\" width=\"32\"><b>%1</b>"
" </td></tr>"
"</table>" ).arg( tr( "This plugin is deprecated" ) );
};
// if ( metadata->value( "status" ) == t.b.d. )
// {
// html += QString( "<table bgcolor=\"#CCCCFF\" cellspacing=\"2\" cellpadding=\"6\" width=\"100%\">"
// " <tr><td width=\"100%\" style=\"color:#000088\"><b>%1</b></td></tr>"
// "</table>" ).arg( tr( "Installing..." ) );
// };

// Now the metadata
html += "<table cellspacing=\"10\" width=\"100%\"><tr><td>";
html += QString( "<h1>%1</h1>" ).arg( metadata->value( "name" ) );

if ( QFileInfo( metadata->value( "icon" ) ).isFile() )
html += "<table cellspacing=\"4\" width=\"100%\"><tr><td>";

QString iconPath = metadata->value( "icon" );
if ( QFileInfo( iconPath ).isFile() )
{
html += QString( "<img src=\"%1\" style=\"float:right;\">" ).arg( metadata->value( "icon" ) );
if ( iconPath.contains( ":/" ) )
{
iconPath = "qrc" + iconPath;
}
else
{
iconPath = "file://" + iconPath;
}
html += QString( "<img src=\"%1\" style=\"float:right;\">" ).arg( iconPath );
}

html += QString( "<h1>%1</h1>" ).arg( metadata->value( "name" ) );

html += QString( "<h3>%1</h3>" ).arg( metadata->value( "description" ) );

if ( ! metadata->value( "about" ).isEmpty() )
{
html += metadata->value( "about" );
}

html += "<table><tr><td align='right' width='100%'>";
if ( ! metadata->value( "average_vote" ).isEmpty() && metadata->value( "average_vote" ).toFloat() )
html += "<br/><br/>";
html += "<div id='stars_bg'/><div id='stars'/>";
html += "<div id='votes'>";
if ( ! metadata->value( "rating_votes" ).isEmpty() )
{
// draw stars
int stars = qRound( metadata->value( "average_vote" ).toFloat() );
for ( int i = 0; i < stars; i++ )
{
html += "<img src=\":/images/themes/default/mIconNew.png\">";
}
html += tr( "<br/>%1 rating vote(s)<br/>" ).arg( metadata->value( "rating_votes" ) );
html += tr( "%1 rating vote(s)" ).arg( metadata->value( "rating_votes" ) );
}
else if ( ! metadata->value( "downloads" ).isEmpty() )
if ( ! ( metadata->value( "rating_votes" ).isEmpty() || metadata->value( "downloads" ).isEmpty() ) )
{
// spacer between description and downloads
html += "<br/>";
html += ", ";
}
if ( ! metadata->value( "downloads" ).isEmpty() )
{
html += tr( "%1 downloads" ).arg( metadata->value( "downloads" ) );
}
html += "</td></tr></table><br/>";
html += "</div>";
html += "<div><a id='send_vote_trigger'/></div>";

html += "</td></tr><tr><td>";
html += "<br/>";

if ( ! metadata->value( "category" ).isEmpty() )
{
Expand Down Expand Up @@ -774,7 +836,9 @@ void QgsPluginManager::showPluginDetails( QStandardItem * item )

html += "</td></tr></table>";

tbDetails->setHtml( html );
html += "</body>";

wvDetails->setHtml( html );

// Set buttonInstall text (and sometimes focus)
buttonInstall->setDefault( false );
Expand Down Expand Up @@ -1009,11 +1073,17 @@ void QgsPluginManager::setCurrentTab( int idx )
QMap<QString, QString>::iterator it = mTabDescriptions.find( tabTitle );
if ( it != mTabDescriptions.end() )
{
QString myStyle = QgsApplication::reportStyleSheet();
tabInfoHTML += "<style>" + myStyle + "</style>";
tabInfoHTML = it.value();
tabInfoHTML += "<style>"
"body, table {"
"margin:4px;"
"font-family:verdana;"
"font-size: 12px;"
"}"
"</style>";
// tabInfoHTML += "<style>" + QgsApplication::reportStyleSheet() + "</style>";
tabInfoHTML += it.value();
}
tbDetails->setHtml( tabInfoHTML );
wvDetails->setHtml( tabInfoHTML );

// disable buttons
buttonInstall->setEnabled( false );
Expand Down Expand Up @@ -1081,6 +1151,34 @@ void QgsPluginManager::on_vwPlugins_doubleClicked( const QModelIndex & theIndex



void QgsPluginManager::on_wvDetails_linkClicked( const QUrl & url )
{
if ( url.scheme() == "rpc2" )
{
if ( url.host() == "plugin.vote" )
{
QString params = url.path();
QString response;
QgsPythonRunner::eval( QString( "pyplugin_installer.instance().sendVote('%1', '%2')" ).arg( params.split( "/" )[1] )
.arg( params.split( "/" )[2] ), response );
if ( response == "True" )
{
pushMessage( tr( "Vote sent successfully" ), QgsMessageBar::INFO );
}
else
{
pushMessage( tr( "Sending vote to the plugin repository failed." ), QgsMessageBar::WARNING );
}
}
}
else
{
QDesktopServices::openUrl( url );
}
}



void QgsPluginManager::on_leFilter_textChanged( QString theText )
{
if ( theText.startsWith( "tag:", Qt::CaseInsensitive ) )
Expand Down
7 changes: 5 additions & 2 deletions src/app/pluginmanager/qgspluginmanager.h
Expand Up @@ -111,11 +111,14 @@ class QgsPluginManager : public QgsOptionsDialogBase, private Ui::QgsPluginManag
void pluginItemChanged( QStandardItem * item );

//! Display details of inactive item too
void on_vwPlugins_clicked( const QModelIndex & );
void on_vwPlugins_clicked( const QModelIndex & index );

//! Load/unload plugin by double click
void on_vwPlugins_doubleClicked( const QModelIndex & index );

//! Handle click in the web wiew
void on_wvDetails_linkClicked( const QUrl & url );

//! Update the filter when user changes the filter expression
void on_leFilter_textChanged( QString theText );

Expand Down Expand Up @@ -165,7 +168,7 @@ class QgsPluginManager : public QgsOptionsDialogBase, private Ui::QgsPluginManag
void clearRepositoryFilter( );

//! show the given message in the Plugin Manager internal message bar
void pushMessage( const QString &text, QgsMessageBar::MessageLevel level, int duration );
void pushMessage( const QString &text, QgsMessageBar::MessageLevel level, int duration = -1 );

protected:
//! Reimplement QgsOptionsDialogBase method as we have a custom window title what would be overwritten by this method
Expand Down

0 comments on commit d398560

Please sign in to comment.