Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Agreement acceptance checkboxes #73

Merged
merged 6 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ ktor-client-json = { module = "io.ktor:ktor-client-json", version.ref = "ktor" }
ktor-client-serialization = { module = "io.ktor:ktor-client-serialization", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-server-core = {module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor"}
ktor-server-netty = {module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor"}
ktor-server-tests = {module = "io.ktor:ktor-server-tests-jvm", version.ref = "ktor"}
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
ktor-server-tests = { module = "io.ktor:ktor-server-tests-jvm", version.ref = "ktor" }

slf4j = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetbrains.research.tasktracker.config

import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.research.tasktracker.config.agreement.AgreementConfig
import org.jetbrains.research.tasktracker.config.content.FinalPageContentConfig
import org.jetbrains.research.tasktracker.config.content.MainPageContentConfig
import org.jetbrains.research.tasktracker.config.content.ServerErrorPageConfig
Expand Down Expand Up @@ -31,7 +32,8 @@ object DefaultConfigsFactory {
FinalPageContentConfig.CONFIG_FILE_PREFIX,
ServerErrorPageConfig.CONFIG_FILE_PREFIX,
SurveyConfig.CONFIG_FILE_PREFIX,
EmotionConfig.CONFIG_FILE_PREFIX
EmotionConfig.CONFIG_FILE_PREFIX,
AgreementConfig.CONFIG_FILE_PREFIX
)

fun createDefaultConfigs() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.jetbrains.research.tasktracker.config

import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.research.tasktracker.config.agreement.AgreementConfig
import org.jetbrains.research.tasktracker.config.content.FinalPageContentConfig
import org.jetbrains.research.tasktracker.config.content.MainPageContentConfig
import org.jetbrains.research.tasktracker.config.content.ServerErrorPageConfig
Expand Down Expand Up @@ -35,6 +36,7 @@ data class MainTaskTrackerConfig(

var scenarioConfig: ScenarioConfig? = null,
var surveyConfig: SurveyConfig? = null,
var agreementConfig: AgreementConfig? = null
) {
fun getAllConfigs() = listOf(
taskContentConfig,
Expand All @@ -46,7 +48,8 @@ data class MainTaskTrackerConfig(
mainPageConfig,
finalPageConfig,
serverErrorPageConfig,
emotionConfig
emotionConfig,
agreementConfig
)

companion object {
Expand Down Expand Up @@ -128,6 +131,12 @@ data class MainTaskTrackerConfig(
mainConfig.emotionConfig, configFile, EmotionConfig::buildConfig, logger
)
}

fileName.startsWith(AgreementConfig.CONFIG_FILE_PREFIX) -> {
mainConfig.agreementConfig = buildBaseConfig(
mainConfig.agreementConfig, configFile, AgreementConfig::buildConfig, logger
)
}
}
}
mainConfig.mainIdeConfig = MainIdeConfig.buildConfig(configFiles)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jetbrains.research.tasktracker.config.agreement

import kotlinx.serialization.Serializable

/**
* text can contain urls, which should be covered in <a> tag.
*/

@Serializable
data class Agreement(val text: String, val required: Boolean = true)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.jetbrains.research.tasktracker.config.agreement

import kotlinx.serialization.Serializable
import org.jetbrains.research.tasktracker.config.BaseConfig
import org.jetbrains.research.tasktracker.config.YamlConfigLoadStrategy
import java.io.File

@Serializable
class AgreementConfig(val agreements: List<Agreement>) : BaseConfig {
override val configName: String
get() = "agreement"

companion object {
const val CONFIG_FILE_PREFIX: String = "agreement"

fun buildConfig(configFile: File): AgreementConfig =
YamlConfigLoadStrategy.load(configFile.readText(), serializer())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.intellij.openapi.wm.ToolWindowFactory
import com.intellij.ui.components.JBPanel
import com.intellij.ui.jcef.JBCefApp
import com.intellij.util.ui.JBUI
import org.jetbrains.concurrency.Promise
import org.jetbrains.research.tasktracker.TaskTrackerPlugin
import org.jetbrains.research.tasktracker.config.content.task.base.Task
import org.jetbrains.research.tasktracker.modelInference.model.EmoModel
Expand All @@ -23,9 +24,10 @@ import org.jetbrains.research.tasktracker.tracking.fileEditor.FileEditorTracker
import org.jetbrains.research.tasktracker.tracking.toolWindow.ToolWindowTracker
import org.jetbrains.research.tasktracker.tracking.webcam.WebCamTracker
import org.jetbrains.research.tasktracker.tracking.webcam.collectAllDevices
import org.jetbrains.research.tasktracker.ui.main.panel.panelStates.welcomePage
import org.jetbrains.research.tasktracker.ui.main.panel.panelStates.agreementAcceptance
import org.jetbrains.research.tasktracker.ui.main.panel.storage.GlobalPluginStorage
import org.jetbrains.research.tasktracker.ui.main.panel.template.*
import org.jetbrains.research.tasktracker.ui.main.panel.template.HtmlTemplate
import org.jetbrains.research.tasktracker.ui.main.panel.template.WebcamChoicePageTemplate
import org.jetbrains.research.tasktracker.util.UIBundle
import java.awt.BorderLayout
import java.awt.FlowLayout
Expand All @@ -39,7 +41,6 @@ import javax.swing.JButton
* Note: This class requires JBCefApp to be supported for proper functioning.
*
*/
@Suppress("TooManyFunctions")
class MainPluginPanelFactory : ToolWindowFactory {
// TODO: init in other place, states can be saved between sessions
private val nextButton = createJButton("ui.button.next")
Expand All @@ -55,7 +56,7 @@ class MainPluginPanelFactory : ToolWindowFactory {
this.project = project
mainWindow = project.getService(MainWindowService::class.java).mainWindow
mainWindow.jComponent.size = JBUI.size(toolWindow.component.width, toolWindow.component.height)
welcomePage()
agreementAcceptance()
val buttonPanel = JBPanel<JBPanel<*>>(FlowLayout()).apply {
add(backButton)
add(nextButton)
Expand Down Expand Up @@ -165,6 +166,12 @@ class MainPluginPanelFactory : ToolWindowFactory {
}
}

/**
* @return **true** if any required field is not filled. **false** otherwise.
*/
fun checkInputs(): Promise<Boolean> =
mainWindow.executeJavaScriptAsync("allChecked()").then { it.toBoolean() }

fun setNextAction(listener: ActionListener) = nextButton.addListener(listener)

fun setBackAction(listener: ActionListener) = backButton.addListener(listener)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class MainPluginWindow(service: MainWindowService) {
fun checkIfRadioButtonChecked(elementId: String): Promise<JsExpressionResult> =
windowBrowser.executeJavaScriptAsync("${getJsElementByIdCommand(elementId)}.checked")

fun executeJavaScriptAsync(code: String) = windowBrowser.executeJavaScriptAsync(code)
fun executeJavaScriptAsync(@Language("JavaScript") code: String) = windowBrowser.executeJavaScriptAsync(code)

fun executeJavascript(
@Language("JavaScript") codeBeforeInject: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,43 @@ import org.jetbrains.research.tasktracker.tracking.activity.ActivityTracker
import org.jetbrains.research.tasktracker.ui.main.panel.MainPluginPanelFactory
import org.jetbrains.research.tasktracker.ui.main.panel.storage.MainPanelStorage
import org.jetbrains.research.tasktracker.ui.main.panel.template.*
import org.jetbrains.research.tasktracker.util.UIBundle
import org.jetbrains.research.tasktracker.util.notifier.notifyError
import org.jetbrains.research.tasktracker.util.survey.SurveyParser

typealias Panel = MainPluginPanelFactory

/**
* A page for collecting user data, and checkboxes for user agreement acceptance.
*/
fun Panel.agreementAcceptance() {
loadBasePage(AgreementTemplate.loadCurrentTemplate(), "ui.button.next", false)
setNextAction {
checkInputs()
mikrise2 marked this conversation as resolved.
Show resolved Hide resolved
.onSuccess {
if (!it) {
welcomePage()
} else {
notifyError(project, UIBundle.message("ui.please.fill"))
}
}
.onError {
error(it.localizedMessage)
}
}
}

/**
* Switches the panel to the plugin description window.
*/
fun Panel.welcomePage() {
loadBasePage(MainPageTemplate.loadCurrentTemplate(), "ui.button.next", false)
loadBasePage(MainPageTemplate.loadCurrentTemplate(), "ui.button.next", true)
setNextAction {
selectTask()
}
setBackAction {
agreementAcceptance()
}
}

// TODO refactor it for many configs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.jetbrains.research.tasktracker.ui.main.panel.template

import org.jetbrains.research.tasktracker.TaskTrackerPlugin
import org.jetbrains.research.tasktracker.config.agreement.Agreement

class AgreementTemplate(private val agreements: List<Agreement>) : HtmlBaseFileTemplate() {
override val contentFilename: String
get() = "agreement"

override val arguments: Array<String>
get() = arrayOf(agreementsToHtml())

override val cssFilename: String
get() = "agreement"

private fun agreementsToHtml() = agreements.mapIndexed { index, element ->
buildString {
append("""<div><input id="$index" type="checkbox" name="$index" """)
append("""${if (element.required) "required" else ""}>""")
append("""<label for="$index">${element.text.withBr()}</label></div>""")
}
}.joinToString("<br>")

private fun String.withBr() = replace(System.lineSeparator(), "<br>")

companion object {
fun loadCurrentTemplate(): AgreementTemplate {
val config = TaskTrackerPlugin.mainConfig.agreementConfig
?: error("agreementConfig has not initialized yet!")
return AgreementTemplate(config.agreements)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.jetbrains.research.tasktracker.util.notifier

import com.intellij.notification.NotificationGroupManager
import com.intellij.notification.NotificationType
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.Nls
import org.jetbrains.research.tasktracker.config.MainTaskTrackerConfig.Companion.PLUGIN_NAME

fun notifyError(project: Project, content: @Nls String?) {
NotificationGroupManager.getInstance().getNotificationGroup(PLUGIN_NAME)
.createNotification(content ?: "Unknown error", NotificationType.ERROR).notify(project)
}
4 changes: 3 additions & 1 deletion ijPlugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
<resource-bundle>messages.MyBundle</resource-bundle>

<extensions defaultExtensionNs="com.intellij">
<toolWindow factoryClass="org.jetbrains.research.tasktracker.ui.main.panel.MainPluginPanelFactory" id="TaskTracker" anchor="right"/>
<toolWindow factoryClass="org.jetbrains.research.tasktracker.ui.main.panel.MainPluginPanelFactory"
id="TaskTracker" anchor="right"/>
<postStartupActivity implementation="org.jetbrains.research.tasktracker.activities.InitActivity"/>
<notificationGroup id="Tasktracker" displayType="BALLOON"/>
</extensions>

<actions>
Expand Down
2 changes: 2 additions & 0 deletions ijPlugin/src/main/resources/messages/UIBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ ui.button.submit=submit
ui.button.welcome=welcome screen

ui.progress.webcam.title=Making Photos Via All Available Devices

ui.please.fill=Please fill all required fields
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
agreements:
- text: |
Multiline
example
required: false
- text: "Example <a href=\"https://github.com/JetBrains-Research/tasktracker-3\">link</a>"
eartser marked this conversation as resolved.
Show resolved Hide resolved
- text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div class="container">
mikrise2 marked this conversation as resolved.
Show resolved Hide resolved
<div class="box">
<p class="big-font">Register in study</p>
<div class="form-group">
<input class="input" type="text" name="name" id="name" placeholder="Name" required>
</div>
<br>
<div class="form-group">
<input class="input" type="email" name="email" id="email" placeholder="Email" required>
</div>
<div class="agreement-container">
<p>I agree to the terms and conditions:</p>
%s
<p><span style="color: red">*</span> - Required field</p>
</div>
</div>
</div>
<script>
let requiredFields = document.querySelectorAll('input[required]')

// Loop through each required input element and add the 'required' class to its parent label
requiredFields.forEach(function (inputElement) {
const parentLabel = inputElement.parentElement.querySelector('label');
if (parentLabel) {
parentLabel.classList.add('required');
}
});

requiredFields.forEach(function (field) {
field.onclick = function () {
if (field.type === "checkbox") {
field.style.outline = '1px solid black'
} else {
field.style.border = '1px solid black';
}
};
});

function allChecked() {
let emptyRequiredFields = [];
requiredFields.forEach(function (field) {
if (field.type === "checkbox") {
if (!field.checked) {
emptyRequiredFields.push(field);
field.style.outline = '1px solid red'
}
} else if (field.value.trim() === "") {
emptyRequiredFields.push(field);
field.style.border = "1px solid red"
}
});
return emptyRequiredFields.length > 0
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 3vmin;
align-items: center;
}

.form-group input {
padding: 12px;
border-radius: 3px;
font-size: 20px;
}

.agreement-container {
align-items: flex-start;
text-align: left;
justify-content: left;
}

.agreement-container div {
display: flex;
align-items: center;
}

.agreement-container div label {
font-size: 15px;
margin-left: 3px;
font-weight: normal !important;
word-wrap: break-word;
}

.required:after {
content: " *";
font-weight: bold;
color: red;
}

.box {
width: 60vmin;
height: 70vmin;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* {
box-sizing: border-box;
}

body {
margin: 0;
-webkit-font-smoothing: antialiased;
Expand Down
Loading