static itemPtr inoreader_source_load_item_from_sourceid (nodePtr node, gchar *sourceId, GHashTable *cache) { gpointer ret = g_hash_table_lookup (cache, sourceId); itemSetPtr itemset; int num = g_hash_table_size (cache); GList *iter; itemPtr item = NULL; if (ret) return item_load (GPOINTER_TO_UINT (ret)); /* skip the top 'num' entries */ itemset = node_get_itemset (node); iter = itemset->ids; while (num--) iter = g_list_next (iter); for (; iter; iter = g_list_next (iter)) { item = item_load (GPOINTER_TO_UINT (iter->data)); if (item) { if (item->sourceId) { /* save to cache */ g_hash_table_insert (cache, g_strdup(item->sourceId), (gpointer) item->id); if (g_str_equal (item->sourceId, sourceId)) { itemset_free (itemset); return item; } } item_unload (item); } } g_warning ("Could not find item for %s!", sourceId); itemset_free (itemset); return NULL; }
static void reedah_feed_subscription_process_update_result (subscriptionPtr subscription, const struct updateResult* const result, updateFlags flags) { if (result->data && result->httpstatus == 200) { GList *items = NULL; jsonApiMapping mapping; /* We expect to get something like this [{"crawlTimeMsec":"1375821312282", "id"::"tag:google.com,reader:2005\/item\/4ee371db36f84de2", "categories":["user\/15724899091976567759\/state\/com.google\/reading-list", "user\/15724899091976567759\/state\/com.google\/fresh"], "title":"Firefox 23 Arrives With New Logo, Mixed Content Blocker, and Network Monitor", "published":1375813680, "updated":1375821312, "alternate":[{"href":"http://rss.slashdot.org/~r/Slashdot/slashdot/~3/Q4450FchLQo/story01.htm","type":"text/html"}], "canonical":[{"href":"http://slashdot.feedsportal.com/c/35028/f/647410/s/2fa2b59c/sc[...]", "type":"text/html"}], "summary":{"direction":"ltr","content":"An anonymous reader writes [...]"}, "author":"Soulskill", "origin":{"streamId":"feed/http://rss.slashdot.org/Slashdot/slashdot","title":"Slashdot", "htmlurl":"http://slashdot.org/" }, [...] */ /* Note: The link and read status cannot be mapped as there might be multiple ones so the callback helper function extracts the first from the array */ mapping.id = "id"; mapping.title = "title"; mapping.link = NULL; mapping.description = "summary/content"; mapping.read = NULL; mapping.updated = "updated"; mapping.author = "author"; mapping.flag = "marked"; mapping.xhtml = TRUE; mapping.negateRead = TRUE; items = json_api_get_items (result->data, "items", &mapping, &reedah_item_callback); /* merge against feed cache */ if (items) { itemSetPtr itemSet = node_get_itemset (subscription->node); subscription->node->newCount = itemset_merge_items (itemSet, items, TRUE /* feed valid */, FALSE /* markAsRead */); itemlist_merge_itemset (itemSet); itemset_free (itemSet); subscription->node->available = TRUE; } else { subscription->node->available = FALSE; g_string_append (((feedPtr)subscription->node->data)->parseErrors, _("Could not parse JSON returned by Reedah API!")); } } else { subscription->node->available = FALSE; } }
static void itemlist_finalize (GObject *object) { itemset_free (itemlist->priv->filter); itemlist_duplicate_list_free (); G_OBJECT_CLASS (parent_class)->finalize (object); }
static void vfolder_free (nodePtr node) { vfolderPtr vfolder = (vfolderPtr) node->data; debug_enter ("vfolder_free"); vfolders = g_slist_remove (vfolders, vfolder); itemset_free (vfolder->itemset); debug_exit ("vfolder_free"); }
/** * Loads a search result into the item list and renders * some info text into the HTML view pane. * * @param searchResult valid search result node * @param searchString search text (or NULL) */ static void search_load_results (nodePtr searchResult, const gchar *searchString) { GString *buffer; itemSetPtr itemSet; nodeViewType viewMode; /* Clear feed and item display and load search results */ feed_list_view_select (NULL); itemlist_unload (FALSE); /* Ensure that we are in a useful viewing mode (3 paned) */ viewMode = itemlist_get_view_mode (); if ((NODE_VIEW_MODE_NORMAL != viewMode) && (NODE_VIEW_MODE_WIDE != viewMode)) itemview_set_layout (NODE_VIEW_MODE_NORMAL); itemSet = node_get_itemset (searchResult); itemlist_load_search_result (itemSet); itemset_free (itemSet); buffer = g_string_new (NULL); htmlview_start_output (buffer, NULL, TRUE, FALSE); g_string_append_printf (buffer, "<div class='content'><h2>"); if (searchString) g_string_append_printf (buffer, ngettext("%d Search Result for \"%s\"", "%d Search Results for \"%s\"", searchResult->itemCount), searchResult->itemCount, searchString); else g_string_append_printf (buffer, ngettext("%d Search Result", "%d Search Results", searchResult->itemCount), searchResult->itemCount); g_string_append_printf (buffer, "</h2><p>"); g_string_append_printf (buffer, _("The item list now contains all items matching the " "specified search pattern. If you want to save this search " "result permanently you can click the \"Search Folder\" button in " "the search dialog and Liferea will add a search folder to your " "feed list.")); g_string_append_printf (buffer, "</p></div>"); htmlview_finish_output (buffer); itemview_display_info (buffer->str); g_string_free (buffer, TRUE); }
static gboolean vfolder_loader_fetch_cb (gpointer user_data, GSList **resultItems) { vfolderPtr vfolder = (vfolderPtr)user_data; itemSetPtr items = g_new0 (struct itemSet, 1); GList *iter; gboolean result; /* 1. Fetch a batch of items */ result = db_itemset_get (items, vfolder->loadOffset, VFOLDER_LOADER_BATCH_SIZE); vfolder->loadOffset += VFOLDER_LOADER_BATCH_SIZE; if (result) { /* 2. Match all items against search folder */ iter = items->ids; while (iter) { gulong id = GPOINTER_TO_UINT (iter->data); itemPtr item = db_item_load (id); if (itemset_check_item (vfolder->itemset, item)) *resultItems = g_slist_append (*resultItems, item); else item_unload (item); iter = g_list_next (iter); } } else { debug1 (DEBUG_CACHE, "search folder '%s' reload complete", vfolder->node->title); vfolder->reloading = FALSE; } itemset_free (items); /* 3. Save items to DB and update UI (except for search results) */ if (vfolder->node) { db_search_folder_add_items (vfolder->node->id, *resultItems); node_update_counters (vfolder->node); ui_node_update (vfolder->node->id); } return result; /* FALSE on last fetch */ }
void inoreader_source_migrate_node(nodePtr node) { /* scan the node for bad ID's, if so, brutally remove the node */ itemSetPtr itemset = node_get_itemset (node); GList *iter = itemset->ids; for (; iter; iter = g_list_next (iter)) { itemPtr item = item_load (GPOINTER_TO_UINT (iter->data)); if (item && item->sourceId) { if (!g_str_has_prefix(item->sourceId, "tag:google.com")) { debug1(DEBUG_UPDATE, "Item with sourceId [%s] will be deleted.", item->sourceId); db_item_remove(GPOINTER_TO_UINT(iter->data)); } } if (item) item_unload (item); } /* cleanup */ itemset_free (itemset); }
static void ttrss_feed_subscription_process_update_result (subscriptionPtr subscription, const struct updateResult* const result, updateFlags flags) { if (result->data && result->httpstatus == 200) { JsonParser *parser = json_parser_new (); if (json_parser_load_from_data (parser, result->data, -1, NULL)) { JsonArray *array = json_node_get_array (json_get_node (json_parser_get_root (parser), "content")); GList *elements = json_array_get_elements (array); GList *iter = elements; GList *items = NULL; /* We expect to get something like this [{"id":118, "unread":true, "marked":false, "updated":1287927675, "is_updated":false, "title":"IBM Says New ...", "link":"http:\/\/rss.slashdot.org\/~r\/Slashdot\/slashdot\/~3\/ALuhNKO3NV4\/story01.htm", "feed_id":"5", "content":"coondoggie writes ..." }, {"id":117, "unread":true, "marked":false, "updated":1287923814, [...] */ while (iter) { JsonNode *node = (JsonNode *)iter->data; itemPtr item = item_new (); gchar *id; const gchar *content; gchar *xhtml; id = g_strdup_printf ("%" G_GINT64_FORMAT, json_get_int (node, "id")); item_set_id (item, id); g_free (id); item_set_title (item, json_get_string (node, "title")); item_set_source (item, json_get_string (node, "link")); content = json_get_string (node, "content"); xhtml = xhtml_extract_from_string (content, NULL); item_set_description (item, xhtml); xmlFree (xhtml); item->time = json_get_int (node, "updated"); if (json_get_bool (node, "unread")) { item->readStatus = FALSE; } else { item->readStatus = TRUE; } if (json_get_bool (node, "marked")) item->flagStatus = TRUE; items = g_list_append (items, (gpointer)item); iter = g_list_next (iter); } g_list_free (elements); /* merge against feed cache */ if (items) { itemSetPtr itemSet = node_get_itemset (subscription->node); gint newCount = itemset_merge_items (itemSet, items, TRUE /* feed valid */, FALSE /* markAsRead */); itemlist_merge_itemset (itemSet); itemset_free (itemSet); feedlist_node_was_updated (subscription->node, newCount); } subscription->node->available = TRUE; } else { subscription->node->available = FALSE; g_string_append (((feedPtr)subscription->node->data)->parseErrors, _("Could not parse JSON returned by TinyTinyRSS API!")); } g_object_unref (parser); } else { subscription->node->available = FALSE; } }
static void notif_libnotify_callback_show_details (NotifyNotification *n, gchar *action, gpointer user_data) { nodePtr node_p; GList *list_p; itemPtr item_p; gchar *labelText_p; gchar *labelText_now_p = NULL; gchar *labelText_prev_p; gchar *labelHeadline_p; const gchar *labelURL_p; gint item_count = 0; g_assert (action != NULL); g_assert (strcmp(action, "show_details") == 0); node_p = node_from_id (user_data); if (node_p) { itemSetPtr itemSet = node_get_itemset (node_p); labelText_now_p = g_strdup (""); /* Gather the feed's headlines */ list_p = itemSet->ids; while (list_p) { item_p = item_load (GPOINTER_TO_UINT (list_p->data)); if (item_p->popupStatus && !item_p->readStatus) { item_p->popupStatus = FALSE; item_count += 1; labelHeadline_p = g_strdup (item_get_title (item_p)); if (labelHeadline_p == NULL ) { labelHeadline_p = g_strdup_printf (_("This news entry has no headline")); } labelURL_p = item_get_base_url (item_p); if (labelURL_p) { labelText_p = g_strdup_printf ("%s <a href='%s'>%s</a>\n", labelHeadline_p, labelURL_p, _("Visit")); } else { labelText_p = g_strdup_printf ("%s\n", labelHeadline_p); } labelText_prev_p = labelText_now_p; labelText_now_p = g_strconcat(labelText_now_p, labelText_p, NULL); g_free(labelHeadline_p); g_free(labelText_p); g_free(labelText_prev_p); } item_unload (item_p); list_p = g_list_next (list_p); } itemset_free (itemSet); if (item_count == 0) { g_free (labelText_now_p); return; } } else { ui_show_error_box(_("This feed does not exist anymore!")); } notify_notification_close (n, NULL); if (node_p) { // notify_notification_update ( n, node_get_title(node_p), labelText_now_p, NULL); // notify_notification_clear_actions(n); n = notify_notification_new (node_get_title (node_p), labelText_now_p, NULL, NULL); notify_notification_set_icon_from_pixbuf (n, node_get_icon (node_p)); notify_notification_set_category (n, "feed"); notify_notification_set_timeout (n, NOTIFY_EXPIRES_NEVER); if (supports_actions) { notify_notification_add_action (n, "open", _("Open feed"), (NotifyActionCallback)notif_libnotify_callback_open, node_p->id, NULL); notify_notification_add_action (n, "mark_read", _("Mark all as read"), (NotifyActionCallback)notif_libnotify_callback_mark_read, node_p->id, NULL); } notify_notification_attach_to_status_icon (n, ui_tray_get_status_icon ()); if (!notify_notification_show (n, NULL)) { g_warning ("libnotify.c - failed to update notification via libnotify\n"); } g_free (labelText_now_p); } }
static void notif_libnotify_node_has_new_items (nodePtr node, gboolean enforced) { itemSetPtr itemSet; GList *iter; NotifyNotification *n; gchar *labelSummary_p; gint item_count = 0; gboolean show_popup_windows; conf_get_bool_value(SHOW_POPUP_WINDOWS, &show_popup_windows); if (!show_popup_windows && !enforced) return; /* Count updated feed */ itemSet = node_get_itemset (node); iter = itemSet->ids; while (iter) { itemPtr item = item_load (GPOINTER_TO_UINT (iter->data)); if (item->popupStatus && !item->readStatus) item_count++; item_unload (item); iter = g_list_next (iter); } itemset_free (itemSet); if (item_count == 0) return; labelSummary_p = g_strdup_printf (ngettext ("<b>%s</b> has <b>%d</b> update", "<b>%s</b> has <b>%d</b> updates", item_count), node_get_title (node), item_count); n = notify_notification_new (_("Feed Update"), labelSummary_p, "liferea", NULL); g_free (labelSummary_p); if (supports_append) { notify_notification_set_hint_string(n, "append", "allow"); } else { notify_notification_set_icon_from_pixbuf (n, node_get_icon (node)); } notify_notification_set_timeout (n, NOTIFY_EXPIRES_DEFAULT); if (supports_actions) { notify_notification_add_action (n, "show_details", _("Show details"), (NotifyActionCallback)notif_libnotify_callback_show_details, node->id, NULL); notify_notification_add_action (n, "open", _("Open feed"), (NotifyActionCallback)notif_libnotify_callback_open, node->id, NULL); notify_notification_add_action (n, "mark_read", _("Mark all as read"), (NotifyActionCallback)notif_libnotify_callback_mark_read, node->id, NULL); } notify_notification_set_category (n, "feed"); notify_notification_attach_to_status_icon (n, ui_tray_get_status_icon ()); if (!notify_notification_show (n, NULL)) g_warning ("notif_libnotify.c - failed to send notification via libnotify"); }
void itemlist_free (void) { itemset_free (itemlist_priv.filter); itemlist_duplicate_list_free (); }