Commit 5e01804e authored by Tristan Claverie's avatar Tristan Claverie

Merge branch '200_gui_show_contract' into 'master'

[gui] Add contract visualization and fetch

[Capture_d_écran_vidéo_de_22-04-2016_22_16_17.webm](/uploads/76b058c513ec72bc2fab81addf7c9027/Capture_d_écran_vidéo_de_22-04-2016_22_16_17.webm)

See merge request !65
parents d148578d 3676530b
Pipeline #879 passed with stages
package main
const about = `DFSS is a simple and secure way to e-sign contracts with a large number of participants, ensuring fairness and minimizing the involvement of the trusted third party (TTP).
This graphical client is provided as part of the DFSS application.
DFSS Team (alphabetical order):
Loïck Bonniot, Axel Caro, Tristan Claverie, Quentin Dauchy,
Quentin Dufour, Yann Porret and Maximilien Richer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`
......@@ -9,5 +9,6 @@
<file>images/time.png</file>
<file>images/digital_signature_pen.png</file>
<file>signform/signform.ui</file>
<file>showcontract/showcontract.ui</file>
</qresource>
</RCC>
......@@ -42,7 +42,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
<height>100</height>
</size>
</property>
<property name="maximumSize">
......
......@@ -35,7 +35,7 @@ func Load() {
viper.Set("file_ca", filepath.Join(path, viper.GetString("filename_ca")))
viper.Set("file_cert", filepath.Join(path, viper.GetString("filename_cert")))
viper.Set("file_key", filepath.Join(path, viper.GetString("filename_key")))
viper.Set("file_config", filepath.Join(path, viper.GetString("filename_config")) + ".json")
viper.Set("file_config", filepath.Join(path, viper.GetString("filename_config"))+".json")
// Fill virtual-only fields
viper.Set("registered", isFileValid(viper.GetString("file_key")))
......
......@@ -2,11 +2,8 @@ package main
import (
"dfss"
"dfss/gui/authform"
"dfss/dfssp/contract"
"dfss/gui/config"
"dfss/gui/contractform"
"dfss/gui/signform"
"dfss/gui/userform"
"github.com/spf13/viper"
"github.com/visualfc/goqt/ui"
)
......@@ -14,7 +11,8 @@ import (
type window struct {
*ui.QMainWindow
current widget
current widget
contract *contract.JSON
}
type widget interface {
......@@ -61,12 +59,39 @@ func main() {
}
func (w *window) addActions() {
newAct := ui.NewActionWithTextParent("&New", w)
newAct.SetShortcuts(ui.QKeySequence_New)
newAct.OnTriggered(w.showNewContractForm)
openAct := ui.NewActionWithTextParent("&Open", w)
openAct.SetShortcuts(ui.QKeySequence_Open)
openAct.OnTriggered(func() {
w.showSignForm()
w.showShowContract("")
})
w.MenuBar().AddAction(openAct)
fetchAct := ui.NewActionWithTextParent("&Fetch", w)
fetchAct.OnTriggered(w.showFetchForm)
aboutAct := ui.NewActionWithTextParent("&About", w)
aboutAct.OnTriggered(func() {
ui.QMessageBoxAbout(w, "About DFSS Client", about)
})
aboutQtAct := ui.NewActionWithTextParent("About &Qt", w)
aboutQtAct.OnTriggered(func() {
ui.QApplicationAboutQt()
})
fileMenu := w.MenuBar().AddMenuWithTitle("&File")
fileMenu.AddAction(newAct)
fileMenu.AddAction(openAct)
fileMenu.AddSeparator()
fileMenu.AddAction(fetchAct)
helpMenu := w.MenuBar().AddMenuWithTitle("&Help")
helpMenu.AddAction(aboutAct)
helpMenu.AddSeparator()
helpMenu.AddAction(aboutQtAct)
}
func (w *window) setScreen(wi widget) {
......@@ -77,34 +102,3 @@ func (w *window) setScreen(wi widget) {
old.DeleteLater()
}
}
func (w *window) showUserForm() {
w.setScreen(userform.NewWidget(func(pwd string) {
w.showAuthForm()
}))
}
func (w *window) showAuthForm() {
w.setScreen(authform.NewWidget(func() {
w.showNewContractForm()
w.addActions()
}))
}
func (w *window) showNewContractForm() {
w.setScreen(contractform.NewWidget())
}
func (w *window) showSignForm() {
home := viper.GetString("home_dir")
filter := "Contract file (*.json);;Any (*.*)"
filename := ui.QFileDialogGetOpenFileNameWithParentCaptionDirFilterSelectedfilterOptions(w, "Select the contract file", home, filter, &filter, 0)
if filename != "" {
config.PasswordDialog(func(err error, pwd string) {
widget := signform.NewWidget(filename, pwd)
if widget != nil {
w.setScreen(widget)
}
})
}
}
package main
import (
"dfss/dfssc/sign"
"dfss/gui/authform"
"dfss/gui/config"
"dfss/gui/contractform"
"dfss/gui/showcontract"
"dfss/gui/signform"
"dfss/gui/userform"
"github.com/spf13/viper"
"github.com/visualfc/goqt/ui"
)
func (w *window) showUserForm() {
w.setScreen(userform.NewWidget(func(pwd string) {
w.showAuthForm()
}))
}
func (w *window) showAuthForm() {
w.setScreen(authform.NewWidget(func() {
w.showNewContractForm()
w.addActions()
}))
}
func (w *window) showNewContractForm() {
w.setScreen(contractform.NewWidget())
}
func (w *window) showShowContract(filename string) {
if filename == "" {
home := viper.GetString("home_dir")
filter := "Contract file (*.json);;Any (*.*)"
filename = ui.QFileDialogGetOpenFileNameWithParentCaptionDirFilterSelectedfilterOptions(w, "Select the contract file", home, filter, &filter, 0)
if filename == "" {
return
}
}
w.contract = showcontract.Load(filename)
if w.contract == nil {
w.showMsgBox("Unable to load file", true)
return
}
w.setScreen(showcontract.NewWidget(w.contract, w.showSignForm))
}
func (w *window) showSignForm() {
config.PasswordDialog(func(err error, pwd string) {
widget := signform.NewWidget(w.contract, pwd)
if widget == nil {
w.showMsgBox("Unable to start the signing procedure", true)
return
}
w.setScreen(widget)
})
}
func (w *window) showFetchForm() {
w.current.Q().SetDisabled(true)
config.PasswordDialog(func(err error, pwd string) {
if err != nil {
w.current.Q().SetDisabled(false)
return
}
dialog := ui.NewInputDialog()
dialog.SetWindowTitle("Fetch a contract from the platform")
dialog.SetLabelText("Please paste the contract identifier here:")
dialog.Show()
dialog.OnAccepted(func() {
uuid := dialog.TextValue()
path := viper.GetString("home_dir") + uuid + ".json"
err := sign.FetchContract(pwd, uuid, path)
if err != nil {
w.showMsgBox(err.Error(), true)
return
}
w.showMsgBox("Contract stored as "+path, false)
w.showShowContract(path)
})
dialog.OnFinished(func(_ int32) {
w.current.Q().SetDisabled(false)
})
})
}
func (w *window) showMsgBox(content string, critical bool) {
m := ui.NewMessageBoxWithParent(w)
m.SetText(content)
if critical {
m.SetWindowTitle("Error")
m.SetIcon(ui.QMessageBox_Critical)
} else {
m.SetWindowTitle("Information")
m.SetIcon(ui.QMessageBox_Information)
}
m.Exec()
}
package showcontract
import (
"encoding/json"
"io/ioutil"
"dfss/dfssp/contract"
"github.com/visualfc/goqt/ui"
)
type Widget struct {
*ui.QWidget
}
func NewWidget(contract *contract.JSON, onSign func()) *Widget {
file := ui.NewFileWithName(":/showcontract/showcontract.ui")
loader := ui.NewUiLoader()
form := loader.Load(file)
fileField := ui.NewLabelFromDriver(form.FindChild("fileField"))
commentField := ui.NewLabelFromDriver(form.FindChild("commentField"))
signersField := ui.NewLabelFromDriver(form.FindChild("signersField"))
informationField := ui.NewLabelFromDriver(form.FindChild("informationField"))
signButton := ui.NewPushButtonFromDriver(form.FindChild("signButton"))
fileField.SetText(contract.File.Name)
commentField.SetText(contract.Comment)
signersField.SetText(getSignersString(contract))
informationField.SetText("Contract #" + contract.UUID + "\nCreated on " + contract.Date.Format("2006-01-02 15:04:05 MST") + ".")
signButton.OnClicked(onSign)
return &Widget{
QWidget: form,
}
}
func (w *Widget) Q() *ui.QWidget {
return w.QWidget
}
func (w *Widget) Tick() {}
// Load loads a file and tries to unmarshall it into a DFSS contract
func Load(filename string) *contract.JSON {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil
}
contract := new(contract.JSON)
err = json.Unmarshal(data, contract)
if err != nil {
return nil
}
return contract
}
func getSignersString(contract *contract.JSON) string {
var s string
for i, signer := range contract.Signers {
if i > 0 {
s += ", "
}
s += signer.Email
}
return s
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShowContract</class>
<widget class="QWidget" name="ShowContract">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>802</width>
<height>457</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>650</width>
<height>350</height>
</size>
</property>
<property name="windowTitle">
<string>Calculator Builder</string>
</property>
<layout class="QGridLayout">
<property name="margin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:20pt;&quot;&gt;Contract&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The following information represents a multiparty contract. Please check it before any signature.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="horizontalSpacing">
<number>30</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>50</number>
</property>
<property name="rightMargin">
<number>50</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="fileLabel">
<property name="text">
<string>Contract file</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="fileField">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fileButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Check with local</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="signersLabel">
<property name="text">
<string>Signers</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="signersField">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="commentLabel">
<property name="text">
<string>Comment</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="commentField">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="informationField">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="signButton">
<property name="text">
<string>Sign now</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
package signform
import (
"encoding/json"
"io/ioutil"
"dfss/dfssc/sign"
"dfss/dfssp/contract"
"github.com/spf13/viper"
......@@ -29,34 +26,26 @@ type Widget struct {
feedback string
}
func NewWidget(filename, pwd string) *Widget {
func NewWidget(contract *contract.JSON, pwd string) *Widget {
loadIcons()
file := ui.NewFileWithName(":/signform/signform.ui")
loader := ui.NewUiLoader()
form := loader.Load(file)
w := &Widget{QWidget: form}
w := &Widget{
QWidget: form,
contract: contract,
}
w.feedbackLabel = ui.NewLabelFromDriver(w.FindChild("mainLabel"))
w.table = ui.NewTableWidgetFromDriver(w.FindChild("signersTable"))
w.progressBar = ui.NewProgressBarFromDriver(w.FindChild("progressBar"))
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil
}
w.contract = new(contract.JSON)
err = json.Unmarshal(data, w.contract)
if err != nil {
return nil
}
m, err := sign.NewSignatureManager(
pwd,
w.contract,
)
if err != nil {
// TODO
return nil
}
w.manager = m
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment