...
 
Commits (13)
......@@ -3,10 +3,12 @@
// enables non-standard api connection (the only one supported at the moment)
#define EPFL
#define USERNAME "afontain"
#mesondefine CACHE_ROOT
// how long we consider the cache «up to date»: for index files,
// the default is one hour; but for regular file, it is 10 years.
#define CACHE_TIME_HTML 3600
#define CACHE_TIME_FILES (10*365*24*3600)
// how verbose the http requests are:
// 0 – none
......@@ -21,3 +23,5 @@
#include "epfl.h"
#endif // EPFL
......@@ -18,20 +18,23 @@
#pragma once
#define EPFL_MAIN_PAGE "https://moodle.epfl.ch/my/"
#define EPFL_TEQUILA_LOGIN "https://moodle.epfl.ch/auth/tequila/index.php"
//#define EPFL_MOODLE_ROOT "https://moodlearchive.epfl.ch/2018-2019/" // doesn't quite work: cannot get course list
#define EPFL_MOODLE_ROOT "https://moodle.epfl.ch"
#define EPFL_MAIN_PAGE EPFL_MOODLE_ROOT"/my/"
#define EPFL_TEQUILA_LOGIN EPFL_MOODLE_ROOT"/auth/tequila/index.php"
#define EPFL_TEQUILA_LOGIN_ACTION "https://tequila.epfl.ch/cgi-bin/tequila/login"
#define EPFL_ENROL_LINK "https://moodle.epfl.ch/enrol/"
#define EPFL_ENROL_LINK EPFL_MOODLE_ROOT"/enrol/"
#define EPFL_COURSE_LINK "https://moodle.epfl.ch/course/view.php?id="
#define EPFL_COURSE_LINK EPFL_MOODLE_ROOT"/course/view.php?id="
#define EPFL_ASSIGNMENT_LINK "https://moodle.epfl.ch/mod/assign/view.php?id="
#define EPFL_FILE_LINK "https://moodle.epfl.ch/mod/resource/view.php?id="
#define EPFL_FOLDERFILE_LINK "https://moodle.epfl.ch/pluginfile.php/%u/mod_folder/content/0/"
#define EPFL_FOLDER_LINK "https://moodle.epfl.ch/mod/folder/view.php?id="
#define EPFL_FORUM_LINK "https://moodle.epfl.ch/mod/forum/view.php?id="
#define EPFL_PAGE_LINK "https://moodle.epfl.ch/mod/page/view.php?id="
#define EPFL_QUIZ_LINK "https://moodle.epfl.ch/mod/quiz/view.php?id="
#define EPFL_URL_LINK "https://moodle.epfl.ch/mod/url/view.php?id="
#define EPFL_ASSIGNMENT_LINK EPFL_MOODLE_ROOT"/mod/assign/view.php?id="
#define EPFL_FILE_LINK EPFL_MOODLE_ROOT"/mod/resource/view.php?id="
#define EPFL_FOLDERFILE_LINK EPFL_MOODLE_ROOT"/pluginfile.php/%u/mod_folder/content/0/"
#define EPFL_FOLDER_LINK EPFL_MOODLE_ROOT"/mod/folder/view.php?id="
#define EPFL_FORUM_LINK EPFL_MOODLE_ROOT"/mod/forum/view.php?id="
#define EPFL_PAGE_LINK EPFL_MOODLE_ROOT"/mod/page/view.php?id="
#define EPFL_QUIZ_LINK EPFL_MOODLE_ROOT"/mod/quiz/view.php?id="
#define EPFL_URL_LINK EPFL_MOODLE_ROOT"/mod/url/view.php?id="
......@@ -129,6 +129,7 @@ moody_sources = [
'src/gui-week.c',
'src/gui-resource.c',
'src/password.c',
'src/settings.c',
'src/utils.c',
]
......
......@@ -84,6 +84,15 @@ update_course_content (GtkWidget *self_widget, gpointer unused)
}
}
void
download_course_content (GtkWidget *self_widget, gpointer bool_download_selected_elements)
{
GuiCourse *self = HDY_GUI_COURSE (self_widget);
gtk_container_foreach (GTK_CONTAINER (self->box_week),
&download_week_content,
bool_download_selected_elements);
}
static void
course_content_obtained_cb (gpointer ptr, gpointer data)
{
......
......@@ -34,5 +34,8 @@ GuiCourse* gui_course_new (struct Course course_info);
void update_course_content (GtkWidget *self_widget, gpointer unused);
void
download_course_content (GtkWidget *self_widget,
gpointer bool_download_selected_elements);
G_END_DECLS
......@@ -21,6 +21,9 @@
struct _GuiPassDialog
{
HdyDialog parent_instance;
MoodSettings *settings;
GtkButton *accept;
GtkButton *cancel;
GtkEntry *user_entry;
......@@ -31,23 +34,18 @@ struct _GuiPassDialog
G_DEFINE_TYPE (GuiPassDialog, gui_pass_dialog, HDY_TYPE_DIALOG)
// not perfect, but I don't want to start creating properties
// and getters and setters for something that works equally well
static pass_cb pass_changed_cb = NULL;
static gpointer pass_changed_cb_data;
GuiPassDialog *
gui_pass_dialog_new (GtkWindow *window,
const gchar *current_username,
pass_cb cb,
gpointer user_data)
gui_pass_dialog_new (GtkWindow *window,
MoodSettings *settings)
{
pass_changed_cb = cb;
pass_changed_cb_data = user_data;
GuiPassDialog *self = g_object_new (HDY_TYPE_GUI_PASS_DIALOG,
"use-header-bar", 1,
NULL);
self->settings = settings;
g_autofree const gchar *current_username;
g_object_get (self->settings, "username", &current_username, NULL);
// prefill the username
gtk_entry_set_text (self->user_entry, current_username);
......@@ -74,14 +72,9 @@ dialog_close_cb (GuiPassDialog *self, gint response)
case GTK_RESPONSE_CANCEL:
break;
case GTK_RESPONSE_ACCEPT:
{
const gchar *username = gtk_entry_get_text (self->user_entry);
const gchar *password = gtk_entry_get_text (self->pass_entry);
if (pass_changed_cb)
pass_changed_cb (pass_changed_cb_data, username, password);
else
g_warn_if_reached ();
}
g_object_set (self->settings,
"username", gtk_entry_get_text (self->user_entry),
"password", gtk_entry_get_text (self->pass_entry), NULL);
break;
default:
g_assert (0);
......
......@@ -18,14 +18,14 @@
#pragma once
#include "settings.h"
#include <gtk/gtk.h>
#define HANDY_USE_UNSTABLE_API
#include <handy.h>
typedef void (*pass_cb) (gpointer user_data, const gchar *username, const gchar *password);
#define HDY_TYPE_GUI_PASS_DIALOG (gui_pass_dialog_get_type())
G_DECLARE_FINAL_TYPE (GuiPassDialog, gui_pass_dialog, HDY, GUI_PASS_DIALOG, HdyDialog)
GuiPassDialog *gui_pass_dialog_new (GtkWindow *win, const gchar *current_username, pass_cb cb, gpointer user_data);
GuiPassDialog *gui_pass_dialog_new (GtkWindow *win, MoodSettings *settings);
......@@ -25,6 +25,17 @@
#define HANDY_USE_UNSTABLE_API
#include <handy.h>
struct FolderFileInfo {
guint folder_id;
gchar *filename;
};
union ResourceInfo {
struct FolderFileInfo folderfileinfo;
const gchar *url;
guint id;
};
struct _GuiResource
{
HdyActionRow parent_instance;
......@@ -150,6 +161,39 @@ activated_cb (GtkListBox* unused, GuiResource *self)
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (self), TRUE);
}
static void
download_folder_content (gpointer self_ptr, gpointer folder_content_ptr) //REWRITEME
{
struct FolderContent *content = folder_content_ptr;
for (guint i=0; i<content->resource_count; i++) {
GuiResource *resource = gui_resource_new (content->resources[i]);
resource_download_content (GTK_WIDGET (resource), NULL);
}
open_folder_cb (self_ptr, folder_content_ptr); // since we know the folder content, display it
}
void
resource_download_content (GtkWidget *self_widget, gpointer unused)
{
GuiResource *self = HDY_GUI_RESOURCE (self_widget);
switch (self->type) {
case RESOURCE_FILE:
moodle_provider_download_file_async (self->id, NULL, self);
break;
case RESOURCE_FOLDERFILE:
if (self->filename && self->id)
moodle_provider_download_file_by_filename_async (self->id, self->filename, NULL, self);
else
g_warning ("That's odd: missing filename and/or id :-/\n");
break;
case RESOURCE_FOLDER:
moodle_provider_get_folder_content_async (download_folder_content, self->id, self);
break;
default:
break;
}
}
static void
gui_resource_set_property (GObject *object,
guint property_id,
......
......@@ -34,4 +34,6 @@ GuiResource *gui_resource_new (struct Resource file_info);
void activated_cb (GtkListBox* unused, GuiResource *self);
void resource_download_content (GtkWidget *self_widget, gpointer unused);
G_END_DECLS
......@@ -63,6 +63,22 @@ update_files (GuiWeek* self)
}
}
void
download_week_content (GtkWidget *self_widget, gpointer bool_download_selected_elements)
{
GuiWeek *self = HDY_GUI_WEEK (self_widget);
if (GPOINTER_TO_INT (bool_download_selected_elements)) {
GList *resources = gtk_list_box_get_selected_rows (self->week_content);
g_list_foreach (resources,
(GFunc)&resource_download_content,
NULL);
} else {
gtk_container_foreach (GTK_CONTAINER (self->week_content),
&resource_download_content,
NULL);
}
}
static void
gui_week_set_property (GObject *object,
guint property_id,
......
......@@ -31,4 +31,8 @@ G_DECLARE_FINAL_TYPE (GuiWeek, gui_week, HDY, GUI_WEEK, GtkBox)
GuiWeek* gui_week_new (struct Week week_info);
void
download_week_content (GtkWidget *self_widget,
gpointer bool_download_selected_elements);
G_END_DECLS
......@@ -20,8 +20,8 @@
#include "gui-pass-dialog.h"
#include "gui-course.h"
#include "data-struct.h"
#include "settings.h"
#include "moodle-provider.h"
#include "password.h"
#include "config.h"
#include <glib/gi18n.h>
......@@ -32,17 +32,18 @@ struct _GuiWindow
{
GtkApplicationWindow parent_instance;
GSettings *settings;
MoodSettings *settings;
struct Course* courses;
guint course_count;
HdyLeaflet *header_box;
HdyLeaflet *content_box;
GtkButton *back;
GtkMenuButton *menu;
GtkMenuButton *primary_menu;
GtkMenuButton *secondary_menu;
//GtkToggleButton *search_button;
GtkStackSidebar *sidebar;
GtkStack *stack;
struct Course* courses;
guint course_count;
//HdySearchBar *search_bar;
//GtkEntry *search_entry;
HdyHeaderGroup *header_group;
......@@ -55,42 +56,45 @@ G_DEFINE_TYPE (GuiWindow, gui_window, GTK_TYPE_APPLICATION_WINDOW)
static void update_moodle_index (GuiWindow *self);
static void
initialize_moodle_provider (GuiWindow *self)
initialize_moodle_provider (MoodSettings *settings)
{
gchar *username, *password;
username = g_settings_get_string (self->settings, "username");
if (!moodle_password_get (username, &password))
g_error ("Could not get password\n");
g_autofree const gchar *username, *password;
g_object_get (settings, "username", &username,
"password", &password, NULL);
moodle_provider_init (MOODLE_MODE_ONLINE, username, password);
}
static void
pass_changed_cb (gpointer ptr, const gchar *username, const gchar *password)
pass_changed_cb (gpointer ptr)
{
GuiWindow *self = HDY_GUI_WINDOW (ptr);
g_settings_set_string (self->settings, "username", username);
if (!moodle_password_set (username, password)) {
g_warning ("Could not set password\n");
}
GuiWindow *self = ptr;
moodle_provider_disconnect ();
initialize_moodle_provider (self);
initialize_moodle_provider (self->settings);
update_moodle_index (self);
}
static void
activate_ask_pass (GSimpleAction *simple,
GVariant *parameter,
gpointer ptr)
gpointer self_ptr)
{
GuiWindow *self = ptr;
const gchar *current_username = g_settings_get_string (self->settings, "username");
self->dialog = gui_pass_dialog_new (GTK_WINDOW (self), current_username, pass_changed_cb, ptr);
GuiWindow *self = self_ptr;
self->dialog = gui_pass_dialog_new (GTK_WINDOW (self), self->settings);
gtk_widget_show (GTK_WIDGET (self->dialog));
}
static void
activate_download_current_course (GSimpleAction *simple,
GVariant *parameter,
gpointer ptr)
{
// get current course
GuiWindow *self = ptr;
download_course_content (gtk_stack_get_visible_child (self->stack),
GINT_TO_POINTER (FALSE));
}
/*
static void
activate_print_string (GSimpleAction *simple,
......@@ -149,7 +153,7 @@ gui_window_key_pressed_cb (GtkWidget *sender,
if (self->course_count > 1) { // that's a little hackish, but works well enough
const gchar *name = gtk_stack_get_visible_child_name (self->stack);
for (guint i=0; i<self->course_count; i++) {
if (!g_strcmp0 (name, self->courses[i].name)) {
if (g_strcmp0 (name, self->courses[i].name) != 0) {
if (i > 0) { // we don't go before the first element
const gchar *target_name = self->courses[i-1].name;
gtk_stack_set_visible_child_name (self->stack, target_name);
......@@ -228,7 +232,12 @@ update_courses (GuiWindow *self)
self->courses[i].name);
}
// ellipsize the labels
// make the secondary menu visible
gtk_widget_show (GTK_WIDGET (self->secondary_menu));
// ellipsize the labels, this is a workaround.
// See https://source.puri.sm/Librem5/libhandy/issues/118 for the bug report
// and https://gitlab.gnugen.ch/afontain/moodle/issues/20
GtkWidget *scrolled_win = gtk_bin_get_child (GTK_BIN (self->sidebar));
GtkWidget *viewport = gtk_bin_get_child (GTK_BIN (scrolled_win));
GtkWidget *label_list = gtk_bin_get_child (GTK_BIN (viewport));
......@@ -289,6 +298,7 @@ gui_window_new (GtkApplication *application)
return g_object_new (HDY_TYPE_GUI_WINDOW, "application", application, NULL);
}
static void
gui_window_finalize (GObject *object)
{
......@@ -297,6 +307,7 @@ gui_window_finalize (GObject *object)
G_OBJECT_CLASS (gui_window_parent_class)->finalize (object);
}
static void
gui_window_class_init (GuiWindowClass *class)
{
......@@ -309,7 +320,8 @@ gui_window_class_init (GuiWindowClass *class)
gtk_widget_class_bind_template_child (widget_class, GuiWindow, header_box);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, content_box);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, back);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, menu);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, primary_menu);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, secondary_menu);
//gtk_widget_class_bind_template_child (widget_class, GuiWindow, search_button);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, sidebar);
gtk_widget_class_bind_template_child (widget_class, GuiWindow, stack);
......@@ -324,7 +336,6 @@ gui_window_class_init (GuiWindowClass *class)
}
static void
update_moodle_index (GuiWindow *self)
{
......@@ -350,18 +361,22 @@ gui_window_init (GuiWindow *self)
self->dialog = NULL;
gtk_widget_init_template (GTK_WIDGET (self));
self->settings = g_settings_new (APP_NAME);
self->settings = mood_settings_new ();
initialize_moodle_provider (self);
g_signal_connect_swapped (self->settings, "notify::password",
G_CALLBACK (pass_changed_cb), self);
const GActionEntry entries[] = {
{ "ask-pass", activate_ask_pass },
{ "ask-pass", activate_ask_pass },
{ "download-current-course", activate_download_current_course },
//{ "print-string", activate_print_string, "s" }
};
g_action_map_add_action_entries (G_ACTION_MAP (self),
entries, G_N_ELEMENTS (entries),
self);
initialize_moodle_provider (self->settings);
hdy_leaflet_set_visible_child_name (self->content_box, "content");
update_header_bar (self);
......
......@@ -32,6 +32,7 @@ typedef struct MoodleProviderState {
const gchar *password;
MoodleOnlineMode online_mode;
gboolean connected;
gboolean connecting;
} MoodleProviderState;
static MoodleProviderState self;
......@@ -118,6 +119,45 @@ get_index_filename (void)
}
static gboolean
cache_is_usable_for (const gchar *filename)
{
g_autoptr (GFile) file = g_file_new_for_uri (filename);
g_autoptr (GFileInfo) fileinfo = g_file_query_info (file,
G_FILE_ATTRIBUTE_TIME_MODIFIED,
G_FILE_QUERY_INFO_NONE,
NULL,
NULL);
if (fileinfo) {
guint64 mtime = g_file_info_get_attribute_uint64 (fileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
guint64 now = g_get_real_time () / 1000000; // that one is in µs
if (g_str_has_suffix (filename, ".html")) {
return CACHE_TIME_HTML > (now - mtime); // if we updated recently enough, that's good
}
return CACHE_TIME_FILES > (now - mtime); // non-html files are less likely to be updated
}
return FALSE; // no file info: this file doesn't exists; we better not rely on cache
}
static void
ensure_connected (void)
{
if (self.connected)
return;
if (self.connecting) {
// wait until we are connected
while (!self.connected) {
usleep (100*1000); // 100ms
}
} else {
// use _connect?
moodle_provider_get_courses_sync (NULL, NULL);
// TODO: update course cache
}
}
static void
request_url_cb (GObject *unused,
GAsyncResult *res,
......@@ -158,7 +198,10 @@ request_url (const gchar *url,
provider_cb final_cb,
gpointer user_data)
{
if (self.online_mode == MOODLE_MODE_ONLINE) {
if (!cache_is_usable_for (filename) && self.online_mode == MOODLE_MODE_ONLINE) {
ensure_connected ();
SoupMessage *msg;
msg = soup_message_new ("GET", url);
......@@ -171,6 +214,8 @@ request_url (const gchar *url,
} else { // offline_mode
g_print ("using cache for \"%s\"\n", filename);
// this is not quite async, but works «fast enough»
g_autoptr (GFile) file = g_file_new_for_uri (filename);
GInputStream *is;
......@@ -199,27 +244,30 @@ moodle_downloaded_file_cb (GInputStream *is,
const char *header = soup_message_headers_get_one (headers, "Content-Disposition");
g_autofree char *real_filename = NULL, *name = NULL;
if (sscanf (header, "inline;" " filename=\"%m[^\"]\"", &name) == 1
|| sscanf (header, "attachment;"" filename=\"%m[^\"]\"", &name) == 1) {
if (sscanf (header, "inline;" " filename=\"%m[^\"]\"", &name) != 1
&& sscanf (header, "attachment;"" filename=\"%m[^\"]\"", &name) != 1) {
g_warning ("could not read header '%s' to get the filename\n", header);
} else { // we could get the file name
real_filename = get_resource_filename (RESOURCE_FILE, 0, name);
g_autofree gchar *path1 = g_filename_from_uri (real_filename, NULL, NULL);
g_autofree gchar *path2 = g_filename_from_uri (filename_looked_for, NULL, NULL);
if (g_strcmp0 (path1, path2)) { // paths are different: make a symlink!
if (g_strcmp0 (path1, path2) != 0) { // paths are different: make a symlink!
g_autoptr (GFile) link_file = g_file_new_for_uri (filename_looked_for);
g_file_make_symbolic_link (link_file, name, NULL, NULL);
// name is the name without the path: we want to make a relative link, so that's perfect
}
} else {
g_warning ("could not read header '%s'\n", header);
}
write_g_input_stream_to_file (is, real_filename ?: filename_looked_for);
}
final_cb (user_data, g_strdup (filename_looked_for));
if (final_cb)
final_cb (user_data, g_strdup (filename_looked_for));
}
void
......@@ -263,6 +311,12 @@ moodle_provider_connect (GInputStream **response)
char *html;
size_t html_size;*/
if (self.connecting)
g_warning ("Connecting while already connecting, that's odd\n");
if (self.connected)
g_warning ("Connecting while already connected, that's odd\n");
self.connecting = TRUE;
msg = soup_message_new ("GET", EPFL_TEQUILA_LOGIN);
soup_session_send_message (self.session, msg);
if (msg->status_code != 200)
......@@ -302,6 +356,7 @@ moodle_provider_connect (GInputStream **response)
}
self.connected = TRUE;
self.connecting = FALSE;
g_object_unref (msg);
return MOODLE_ERROR_OK;
......@@ -322,7 +377,8 @@ got_course_content_cb (GInputStream *is,
struct CourseContent content = moodle_parse_course_content (html, html_size, filename);
if (should_save_to_disk)
write_file_async (filename, g_steal_pointer (&html), html_size);
final_cb (user_data, &content);
if (final_cb)
final_cb (user_data, &content);
}
......@@ -335,12 +391,13 @@ got_folder_content_cb (GInputStream *is,
gpointer user_data)
{
g_autofree gchar *html = NULL;
gsize html_size;
gsize html_size;
read_g_input_stream (is, &html, &html_size);
struct FolderContent content = moodle_parse_folder_content (html, html_size, filename);
if (should_save_to_disk)
write_file_async (filename, g_steal_pointer (&html), html_size);
final_cb (user_data, &content);
if (final_cb)
final_cb (user_data, &content);
}
void
......@@ -404,6 +461,7 @@ moodle_provider_init (MoodleOnlineMode mode,
{
self.online_mode = mode;
self.connected = FALSE;
self.connecting = FALSE;
moodle_parser_init ();
......@@ -432,4 +490,3 @@ moodle_provider_init (MoodleOnlineMode mode,
SOUP_SESSION_FEATURE (self.logger));
} else self.logger = NULL;
}
......@@ -25,6 +25,7 @@ typedef void (*courses_cb) (gpointer user_data, struct Courses);
typedef void (*provider_cb) (gpointer user_data, gpointer retrieved_content);
// retrieved_content will be a struct Courses *, struct FolderContent *
// according to the called function name.
// when giving to a function a provider_cb, you can pass NULL instead.
enum moodle_error {
MOODLE_ERROR_OK,
......
/*
* Copyright 2019, Antoine Fontaine <antoine.fontaine@epfl.ch>
* I release this program under GNU GPLv3+.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "settings.h"
#include "password.h"
#include "config.h"
enum {
PROP_0,
PROP_SETTING_USERNAME,
PROP_SETTING_PASSWORD,
PROP_SETTING_USE_CACHE,
N_SETTINGS
};
GParamSpec *mood_settings_properties[N_SETTINGS] = { NULL, };
struct _MoodSettings
{
GObject parent_instance;
const gchar *username;
gboolean use_cache;
};
G_DEFINE_TYPE (MoodSettings, mood_settings, G_TYPE_OBJECT)
MoodSettings *
mood_settings_new (void)
{
return g_object_new (MOOD_TYPE_SETTINGS, NULL);
}
static void
mood_settings_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
MoodSettings *self = MOOD_MOOD_SETTINGS (object);
switch (property_id) {
case PROP_SETTING_USERNAME:
self->username = g_strdup(g_value_get_string (value));
break;
case PROP_SETTING_PASSWORD:
moodle_password_set (self->username, g_value_get_string (value));
break;
case PROP_SETTING_USE_CACHE:
self->use_cache = g_value_get_boolean (value);
break;
default:
// We don't have any other property...
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
mood_settings_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
MoodSettings *self = MOOD_MOOD_SETTINGS (object);
switch (property_id) {
case PROP_SETTING_USERNAME:
if (!self->username)
g_warning ("That read isn't going well\n");
g_value_set_string (value, self->username);
break;
case PROP_SETTING_PASSWORD: {
gchar *password;
if (!moodle_password_get (self->username, &password))
g_error ("impossible to get saved password\n");
g_value_set_string (value, password);
break;
}
case PROP_SETTING_USE_CACHE:
g_value_set_boolean (value, self->use_cache);
break;
default:
// We don't have any other property...
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
mood_settings_class_init (MoodSettingsClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
//object_class->finalize = &mood_settings_finalize;
mood_settings_properties[PROP_SETTING_USERNAME] =
g_param_spec_string ("username", "username",
"The username we use to connect to moodle. At EPFL, this is your gaspard account.",
NULL,
G_PARAM_WRITABLE|G_PARAM_READABLE);
mood_settings_properties[PROP_SETTING_PASSWORD] =
g_param_spec_string ("password", "password",
"Your account's password",
NULL,
G_PARAM_WRITABLE|G_PARAM_READABLE);
mood_settings_properties[PROP_SETTING_USE_CACHE] =
g_param_spec_boolean ("use_cache", "use_cache",
"Whether to save to disk the responses the moodle provider gets",
TRUE,
G_PARAM_WRITABLE|G_PARAM_READABLE);
object_class->set_property = &mood_settings_set_property;
object_class->get_property = &mood_settings_get_property;
g_object_class_install_properties (object_class,
N_SETTINGS,
mood_settings_properties);
}
static void
mood_settings_init (MoodSettings *self)
{
//self->settings = g_settings_new (APP_NAME);
GSettings *settings = g_settings_new (APP_NAME);
self->username = NULL;
self->use_cache = -1;
g_settings_bind (settings, "username",
self, "username",
G_SETTINGS_BIND_DEFAULT);
g_settings_bind (settings, "use-cache",
self, "use-cache",
G_SETTINGS_BIND_DEFAULT);
}
\ No newline at end of file
/*
* Copyright 2019, Antoine Fontaine <antoine.fontaine@epfl.ch>
* I release this program under GNU GPLv3+.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define MOOD_TYPE_SETTINGS (mood_settings_get_type())
G_DECLARE_FINAL_TYPE (MoodSettings, mood_settings, MOOD, MOOD_SETTINGS, GObject)
MoodSettings *mood_settings_new (void);
G_END_DECLS
......@@ -3,7 +3,7 @@
<interface>
<requires lib="gtk+" version="3.16"/>
<requires lib="libhandy" version="0.0"/>
<object class="GtkPopoverMenu" id="menu_popover">
<object class="GtkPopoverMenu" id="primary_menu_popover">
<child>
<object class="GtkBox">
<property name="visible">True</property>
......@@ -44,9 +44,32 @@
</object>
</child>
</object>
<object class="GtkPopoverMenu" id="secondary_menu_popover">
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="margin">10</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkModelButton" id="download">
<property name="text" translatable="yes">Download course files</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action-name">win.download-current-course</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
<template class="GuiWindow" parent="GtkApplicationWindow">
<property name="can_focus">False</property>
<property name="title">Handy Moodle</property>
<property name="title">Moody</property>
<property name="default_width">360</property>
<property name="default_height">576</property>
<signal name="key-press-event" handler="key_pressed_cb" after="yes" swapped="no"/>
......@@ -65,8 +88,27 @@
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Handy Moodle</property>
<property name="title">Moody</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkMenuButton" id="primary_menu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="popover">primary_menu_popover</property>
<child>
<object class="GtkImage" id="open_primary_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
<packing>
<property name="name">sidebar</property>
......@@ -112,16 +154,16 @@
</packing>
</child>
<child>
<object class="GtkMenuButton" id="menu">
<property name="visible">True</property>
<object class="GtkMenuButton" id="secondary_menu">
<property name="visible">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="popover">menu_popover</property>
<property name="popover">secondary_menu_popover</property>
<child>
<object class="GtkImage" id="open_menu">
<object class="GtkImage" id="open_secondary_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
<property name="icon_name">view-more-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
......@@ -217,7 +259,7 @@
<property name="opacity">0.7</property>
<property name="halign">center</property>
<property name="margin_bottom">12</property>
<property name="label" translatable="yes">Welcome to Handy Moodle</property>
<property name="label" translatable="yes">Welcome!</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<attributes>
......@@ -236,7 +278,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="opacity">0.7</property>
<property name="label" translatable="yes">This is a very αλφα version.</property>
<property name="label" translatable="yes">You should set you username to use Moody :-)</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="use_markup">True</property>
......@@ -335,3 +377,5 @@