Showing preview only (3,205K chars total). Download the full file or copy to clipboard to get everything.
Repository: Azareal/Gosora
Branch: master
Commit: 553e66b59003
Files: 695
Total size: 2.9 MB
Directory structure:
gitextract_v2tdrp1t/
├── .codebeatignore
├── .codeclimate.yml
├── .eslintrc.json
├── .gitignore
├── .htaccess
├── .travis.yml
├── .vscode/
│ └── settings.json
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── TODO.md
├── attachs/
│ └── filler.txt
├── backups/
│ └── filler.txt
├── build-linux
├── build-linux-nowebsockets
├── build-nowebsockets.bat
├── build.bat
├── build_templates.bat
├── cmd/
│ ├── common_hook_gen/
│ │ └── hookgen.go
│ ├── elasticsearch/
│ │ └── setup.go
│ ├── hook_gen/
│ │ └── main.go
│ ├── hook_stub_gen/
│ │ └── main.go
│ ├── install/
│ │ └── install.go
│ └── query_gen/
│ ├── build.bat
│ ├── main.go
│ ├── run.bat
│ ├── spitter.go
│ └── tables.go
├── common/
│ ├── activity_stream.go
│ ├── activity_stream_matches.go
│ ├── alerts/
│ │ └── tmpls.go
│ ├── alerts.go
│ ├── analytics.go
│ ├── attachments.go
│ ├── audit_logs.go
│ ├── auth.go
│ ├── cache.go
│ ├── common.go
│ ├── common_easyjson.tgo
│ ├── conversations.go
│ ├── convos_posts.go
│ ├── counters/
│ │ ├── agents.go
│ │ ├── common.go
│ │ ├── forums.go
│ │ ├── langs.go
│ │ ├── memory.go
│ │ ├── performance.go
│ │ ├── posts.go
│ │ ├── referrers.go
│ │ ├── requests.go
│ │ ├── routes.go
│ │ ├── systems.go
│ │ ├── topics.go
│ │ └── topics_views.go
│ ├── disk.go
│ ├── email.go
│ ├── email_store.go
│ ├── errors.go
│ ├── extend.go
│ ├── files.go
│ ├── forum.go
│ ├── forum_actions.go
│ ├── forum_perms.go
│ ├── forum_perms_store.go
│ ├── forum_store.go
│ ├── gauth/
│ │ └── authenticator.go
│ ├── group.go
│ ├── group_store.go
│ ├── ip_search.go
│ ├── likes.go
│ ├── menu_item_store.go
│ ├── menu_store.go
│ ├── menus.go
│ ├── meta/
│ │ └── meta_store.go
│ ├── mfa_store.go
│ ├── misc_logs.go
│ ├── module_ottojs.go
│ ├── no_websockets.go
│ ├── null_reply_cache.go
│ ├── null_topic_cache.go
│ ├── null_user_cache.go
│ ├── page_store.go
│ ├── pages.go
│ ├── parser.go
│ ├── password_reset.go
│ ├── permissions.go
│ ├── phrases/
│ │ └── phrases.go
│ ├── pluginlangs.go
│ ├── poll.go
│ ├── poll_cache.go
│ ├── poll_store.go
│ ├── profile_reply.go
│ ├── profile_reply_store.go
│ ├── promotions.go
│ ├── ratelimit.go
│ ├── recalc.go
│ ├── relations.go
│ ├── reply.go
│ ├── reply_cache.go
│ ├── reply_store.go
│ ├── report_store.go
│ ├── routes_common.go
│ ├── search.go
│ ├── settings.go
│ ├── site.go
│ ├── statistics.go
│ ├── subscription.go
│ ├── tasks.go
│ ├── template_init.go
│ ├── templates/
│ │ ├── context.go
│ │ ├── minifiers.go
│ │ └── templates.go
│ ├── thaw.go
│ ├── theme.go
│ ├── theme_list.go
│ ├── thumbnailer.go
│ ├── tickloop.go
│ ├── topic.go
│ ├── topic_cache.go
│ ├── topic_list.go
│ ├── topic_store.go
│ ├── user.go
│ ├── user_cache.go
│ ├── user_store.go
│ ├── utils.go
│ ├── weak_passwords.go
│ ├── websockets.go
│ ├── widget.go
│ ├── widget_search_and_filter.go
│ ├── widget_store.go
│ ├── widget_wol.go
│ ├── widget_wol_context.go
│ ├── widgets.go
│ ├── word_filters.go
│ ├── ws_hub.go
│ └── ws_user.go
├── config/
│ ├── config_example.json
│ ├── emoji_default.json
│ ├── filler.txt
│ └── weakpass_default.json
├── database.go
├── dev-update-linux
├── dev-update-travis
├── dev-update.bat
├── docs/
│ ├── configuration.md
│ ├── custom_pages.md
│ ├── emoji.md
│ ├── installation.md
│ ├── internationalisation.md
│ ├── landing_page.md
│ ├── templates.md
│ ├── updating.md
│ └── weak_passwords.md
├── experimental/
│ ├── config.json
│ ├── counterTree/
│ │ ├── tree.go
│ │ └── tree_test.go
│ ├── module_lua.go
│ ├── module_v8js.go
│ ├── new-replybit.html
│ ├── new-update.bat
│ ├── plugin_geoip.go
│ ├── plugin_sendmail.go
│ ├── theme-ext.json
│ └── theme-ext.xml
├── extend/
│ ├── adventure/
│ │ ├── lib/
│ │ │ ├── adventure.go
│ │ │ └── adventure_store.go
│ │ ├── plugin.json
│ │ └── prebuild/
│ │ └── filler.txt
│ ├── filler.go
│ ├── guilds/
│ │ ├── lib/
│ │ │ ├── guild_store.go
│ │ │ └── guilds.go
│ │ ├── plugin.json
│ │ ├── plugin_guilds.go
│ │ └── prebuild/
│ │ └── filler.txt
│ ├── heytherejs/
│ │ ├── main.js
│ │ └── plugin.json
│ ├── plugin_adventure.go
│ ├── plugin_bbcode.go
│ ├── plugin_heythere.go
│ ├── plugin_hyperdrive.go
│ ├── plugin_markdown.go
│ └── plugin_skeleton.go
├── gen_mssql.go
├── gen_mysql.go
├── gen_pgsql.go
├── gen_router.go
├── gen_tables.go
├── general_test.go
├── go.mod
├── go.sum
├── gosora_example.service
├── install/
│ ├── install.go
│ ├── mssql.go
│ ├── mysql.go
│ ├── pgsql.go
│ └── utils.go
├── install-docker
├── install-linux
├── install.bat
├── langs/
│ └── english.json
├── last_version.txt
├── logs/
│ └── filler.txt
├── main.go
├── migrations/
│ └── filler.txt
├── misc_test.go
├── mssql.go
├── mysql.go
├── old_router.go
├── pages/
│ └── page_test.html
├── parser_test.go
├── patcher/
│ ├── main.go
│ ├── patches.go
│ └── utils.go
├── pgsql.go
├── plugin_test.go
├── pre-run-linux
├── public/
│ ├── EQCSS.js
│ ├── Sortable-1.4.0/
│ │ ├── .editorconfig
│ │ ├── .gitignore
│ │ ├── .jshintrc
│ │ ├── CONTRIBUTING.md
│ │ ├── README.md
│ │ ├── Sortable.js
│ │ ├── bower.json
│ │ ├── component.json
│ │ ├── jquery.binding.js
│ │ └── package.json
│ ├── account.js
│ ├── analytics.js
│ ├── chartist/
│ │ ├── chartist-plugin-legend.css
│ │ └── chartist.css
│ ├── convo.js
│ ├── font-awesome-4.7.0/
│ │ └── fonts/
│ │ └── FontAwesome.otf
│ ├── global.js
│ ├── init.js
│ ├── jquery-emojiarea/
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── emojis.js
│ │ ├── jquery.emojiarea.css
│ │ └── jquery.emojiarea.js
│ ├── member.js
│ ├── panel_forum_edit.js
│ ├── panel_forums.js
│ ├── panel_menu_items.js
│ ├── profile_member.js
│ ├── register.js
│ ├── templates/
│ │ └── filler.txt
│ ├── trumbowyg/
│ │ └── ui/
│ │ ├── trumbowyg.css
│ │ └── trumbowyg.custom.css
│ └── widgets.js
├── pubnot/
│ ├── chartist/
│ │ ├── chartist-plugin-legend.css
│ │ ├── chartist.css
│ │ ├── chartist.js
│ │ └── scss/
│ │ ├── chartist.scss
│ │ └── settings/
│ │ └── _chartist-settings.scss
│ ├── font-awesome-4.7.0/
│ │ ├── css/
│ │ │ └── font-awesome.css
│ │ └── fonts/
│ │ └── FontAwesome.otf
│ └── trumbowyg/
│ ├── plugins/
│ │ ├── base64/
│ │ │ └── trumbowyg.base64.js
│ │ ├── cleanpaste/
│ │ │ └── trumbowyg.cleanpaste.js
│ │ ├── colors/
│ │ │ ├── trumbowyg.colors.js
│ │ │ └── ui/
│ │ │ ├── sass/
│ │ │ │ └── trumbowyg.colors.scss
│ │ │ └── trumbowyg.colors.css
│ │ ├── emoji/
│ │ │ ├── trumbowyg.emoji.js
│ │ │ └── ui/
│ │ │ ├── sass/
│ │ │ │ └── trumbowyg.emoji.scss
│ │ │ └── trumbowyg.emoji.css
│ │ ├── insertaudio/
│ │ │ └── trumbowyg.insertaudio.js
│ │ ├── noembed/
│ │ │ └── trumbowyg.noembed.js
│ │ ├── pasteimage/
│ │ │ └── trumbowyg.pasteimage.js
│ │ ├── preformatted/
│ │ │ └── trumbowyg.preformatted.js
│ │ ├── table/
│ │ │ └── trumbowyg.table.js
│ │ ├── template/
│ │ │ └── trumbowyg.template.js
│ │ └── upload/
│ │ └── trumbowyg.upload.js
│ ├── trumbowyg.js
│ └── ui/
│ ├── sass/
│ │ └── trumbowyg.scss
│ ├── trumbowyg.css
│ └── trumbowyg.custom.css
├── query_gen/
│ ├── acc_builders.go
│ ├── accumulator.go
│ ├── builder.go
│ ├── install.go
│ ├── micro_builders.go
│ ├── mssql.go
│ ├── mysql.go
│ ├── pgsql.go
│ ├── querygen.go
│ ├── transaction.go
│ ├── utils.go
│ └── utils_test.go
├── quick-update-linux
├── rev_templates.go
├── router.go
├── router_gen/
│ ├── build.bat
│ ├── main.go
│ ├── misc_test.go
│ ├── prec.go
│ ├── route_group.go
│ ├── route_impl.go
│ ├── route_subset.go
│ ├── router.go
│ ├── routes.go
│ └── run.bat
├── routes/
│ ├── account.go
│ ├── api.go
│ ├── attachments.go
│ ├── common.go
│ ├── convos.go
│ ├── forum.go
│ ├── forum_list.go
│ ├── misc.go
│ ├── moderate.go
│ ├── panel/
│ │ ├── analytics.go
│ │ ├── backups.go
│ │ ├── common.go
│ │ ├── dashboard.go
│ │ ├── debug.go
│ │ ├── forums.go
│ │ ├── groups.go
│ │ ├── logs.go
│ │ ├── pages.go
│ │ ├── plugins.go
│ │ ├── settings.go
│ │ ├── themes.go
│ │ ├── users.go
│ │ └── word_filters.go
│ ├── poll.go
│ ├── profile.go
│ ├── profile_reply.go
│ ├── reply.go
│ ├── reports.go
│ ├── stubs.go
│ ├── topic.go
│ ├── topic_list.go
│ └── user.go
├── routes.go
├── run-linux
├── run-linux-nowebsockets
├── run-linux-tests
├── run-nowebsockets.bat
├── run.bat
├── run_mssql.bat
├── run_tests.bat
├── run_tests_mssql.bat
├── schema/
│ ├── mssql/
│ │ ├── inserts.sql
│ │ ├── query_activity_stream.sql
│ │ ├── query_activity_stream_matches.sql
│ │ ├── query_activity_subscriptions.sql
│ │ ├── query_administration_logs.sql
│ │ ├── query_attachments.sql
│ │ ├── query_conversations.sql
│ │ ├── query_conversations_participants.sql
│ │ ├── query_conversations_posts.sql
│ │ ├── query_emails.sql
│ │ ├── query_forums.sql
│ │ ├── query_forums_actions.sql
│ │ ├── query_forums_permissions.sql
│ │ ├── query_likes.sql
│ │ ├── query_login_logs.sql
│ │ ├── query_memchunks.sql
│ │ ├── query_menu_items.sql
│ │ ├── query_menus.sql
│ │ ├── query_meta.sql
│ │ ├── query_moderation_logs.sql
│ │ ├── query_pages.sql
│ │ ├── query_password_resets.sql
│ │ ├── query_perfchunks.sql
│ │ ├── query_plugins.sql
│ │ ├── query_polls.sql
│ │ ├── query_polls_options.sql
│ │ ├── query_polls_voters.sql
│ │ ├── query_polls_votes.sql
│ │ ├── query_postchunks.sql
│ │ ├── query_registration_logs.sql
│ │ ├── query_replies.sql
│ │ ├── query_revisions.sql
│ │ ├── query_settings.sql
│ │ ├── query_sync.sql
│ │ ├── query_themes.sql
│ │ ├── query_topicchunks.sql
│ │ ├── query_topics.sql
│ │ ├── query_updates.sql
│ │ ├── query_users.sql
│ │ ├── query_users_2fa_keys.sql
│ │ ├── query_users_avatar_queue.sql
│ │ ├── query_users_blocks.sql
│ │ ├── query_users_groups.sql
│ │ ├── query_users_groups_promotions.sql
│ │ ├── query_users_groups_scheduler.sql
│ │ ├── query_users_replies.sql
│ │ ├── query_viewchunks.sql
│ │ ├── query_viewchunks_agents.sql
│ │ ├── query_viewchunks_forums.sql
│ │ ├── query_viewchunks_langs.sql
│ │ ├── query_viewchunks_referrers.sql
│ │ ├── query_viewchunks_systems.sql
│ │ ├── query_widgets.sql
│ │ └── query_word_filters.sql
│ ├── mysql/
│ │ ├── inserts.sql
│ │ ├── query_activity_stream.sql
│ │ ├── query_activity_stream_matches.sql
│ │ ├── query_activity_subscriptions.sql
│ │ ├── query_administration_logs.sql
│ │ ├── query_attachments.sql
│ │ ├── query_conversations.sql
│ │ ├── query_conversations_participants.sql
│ │ ├── query_conversations_posts.sql
│ │ ├── query_emails.sql
│ │ ├── query_forums.sql
│ │ ├── query_forums_actions.sql
│ │ ├── query_forums_permissions.sql
│ │ ├── query_likes.sql
│ │ ├── query_login_logs.sql
│ │ ├── query_memchunks.sql
│ │ ├── query_menu_items.sql
│ │ ├── query_menus.sql
│ │ ├── query_meta.sql
│ │ ├── query_moderation_logs.sql
│ │ ├── query_pages.sql
│ │ ├── query_password_resets.sql
│ │ ├── query_perfchunks.sql
│ │ ├── query_plugins.sql
│ │ ├── query_polls.sql
│ │ ├── query_polls_options.sql
│ │ ├── query_polls_voters.sql
│ │ ├── query_polls_votes.sql
│ │ ├── query_postchunks.sql
│ │ ├── query_registration_logs.sql
│ │ ├── query_replies.sql
│ │ ├── query_revisions.sql
│ │ ├── query_settings.sql
│ │ ├── query_sync.sql
│ │ ├── query_themes.sql
│ │ ├── query_topicchunks.sql
│ │ ├── query_topics.sql
│ │ ├── query_updates.sql
│ │ ├── query_users.sql
│ │ ├── query_users_2fa_keys.sql
│ │ ├── query_users_avatar_queue.sql
│ │ ├── query_users_blocks.sql
│ │ ├── query_users_groups.sql
│ │ ├── query_users_groups_promotions.sql
│ │ ├── query_users_groups_scheduler.sql
│ │ ├── query_users_replies.sql
│ │ ├── query_viewchunks.sql
│ │ ├── query_viewchunks_agents.sql
│ │ ├── query_viewchunks_forums.sql
│ │ ├── query_viewchunks_langs.sql
│ │ ├── query_viewchunks_referrers.sql
│ │ ├── query_viewchunks_systems.sql
│ │ ├── query_widgets.sql
│ │ └── query_word_filters.sql
│ ├── pgsql/
│ │ ├── inserts.sql
│ │ ├── query_activity_stream.sql
│ │ ├── query_activity_stream_matches.sql
│ │ ├── query_activity_subscriptions.sql
│ │ ├── query_administration_logs.sql
│ │ ├── query_attachments.sql
│ │ ├── query_conversations.sql
│ │ ├── query_conversations_participants.sql
│ │ ├── query_conversations_posts.sql
│ │ ├── query_emails.sql
│ │ ├── query_forums.sql
│ │ ├── query_forums_actions.sql
│ │ ├── query_forums_permissions.sql
│ │ ├── query_likes.sql
│ │ ├── query_login_logs.sql
│ │ ├── query_memchunks.sql
│ │ ├── query_menu_items.sql
│ │ ├── query_menus.sql
│ │ ├── query_meta.sql
│ │ ├── query_moderation_logs.sql
│ │ ├── query_pages.sql
│ │ ├── query_password_resets.sql
│ │ ├── query_perfchunks.sql
│ │ ├── query_plugins.sql
│ │ ├── query_polls.sql
│ │ ├── query_polls_options.sql
│ │ ├── query_polls_votes.sql
│ │ ├── query_postchunks.sql
│ │ ├── query_registration_logs.sql
│ │ ├── query_replies.sql
│ │ ├── query_revisions.sql
│ │ ├── query_settings.sql
│ │ ├── query_sync.sql
│ │ ├── query_themes.sql
│ │ ├── query_topicchunks.sql
│ │ ├── query_topics.sql
│ │ ├── query_updates.sql
│ │ ├── query_users.sql
│ │ ├── query_users_2fa_keys.sql
│ │ ├── query_users_avatar_queue.sql
│ │ ├── query_users_blocks.sql
│ │ ├── query_users_groups.sql
│ │ ├── query_users_groups_promotions.sql
│ │ ├── query_users_groups_scheduler.sql
│ │ ├── query_users_replies.sql
│ │ ├── query_viewchunks.sql
│ │ ├── query_viewchunks_agents.sql
│ │ ├── query_viewchunks_forums.sql
│ │ ├── query_viewchunks_langs.sql
│ │ ├── query_viewchunks_referrers.sql
│ │ ├── query_viewchunks_systems.sql
│ │ ├── query_widgets.sql
│ │ └── query_word_filters.sql
│ └── schema.json
├── templates/
│ ├── account.html
│ ├── account_blocked.html
│ ├── account_logins.html
│ ├── account_menu.html
│ ├── account_own_edit.html
│ ├── account_own_edit_email.html
│ ├── account_own_edit_level.html
│ ├── account_own_edit_mfa.html
│ ├── account_own_edit_mfa_setup.html
│ ├── account_own_edit_password.html
│ ├── account_own_edit_privacy.html
│ ├── account_test.html
│ ├── alert.html
│ ├── are_you_sure.html
│ ├── convo.html
│ ├── convo_row.html
│ ├── convo_row_alt.html
│ ├── convos.html
│ ├── create_convo.html
│ ├── create_topic.html
│ ├── custom_page.html
│ ├── error.html
│ ├── footer.html
│ ├── forum.html
│ ├── forum_gallery.html
│ ├── forums.html
│ ├── guilds_create_guild.html
│ ├── guilds_css.html
│ ├── guilds_guild_list.html
│ ├── guilds_member_list.html
│ ├── guilds_view_guild.html
│ ├── header.html
│ ├── ip_search.html
│ ├── level_list.html
│ ├── login.html
│ ├── login_mfa_verify.html
│ ├── menu_alerts.html
│ ├── menu_item.html
│ ├── notice.html
│ ├── overrides/
│ │ └── filler.txt
│ ├── overview.html
│ ├── paginator.html
│ ├── paginator_mod.html
│ ├── panel.html
│ ├── panel_adminlogs.html
│ ├── panel_analytics_active_memory.html
│ ├── panel_analytics_agent_views.html
│ ├── panel_analytics_agents.html
│ ├── panel_analytics_forum_views.html
│ ├── panel_analytics_forums.html
│ ├── panel_analytics_lang_views.html
│ ├── panel_analytics_langs.html
│ ├── panel_analytics_memory.html
│ ├── panel_analytics_performance.html
│ ├── panel_analytics_posts.html
│ ├── panel_analytics_referrer_views.html
│ ├── panel_analytics_referrers.html
│ ├── panel_analytics_route_views.html
│ ├── panel_analytics_routes.html
│ ├── panel_analytics_routes_perf.html
│ ├── panel_analytics_script.html
│ ├── panel_analytics_script_memory.html
│ ├── panel_analytics_script_perf.html
│ ├── panel_analytics_system_views.html
│ ├── panel_analytics_systems.html
│ ├── panel_analytics_time_range.html
│ ├── panel_analytics_time_range_month.html
│ ├── panel_analytics_topics.html
│ ├── panel_analytics_views.html
│ ├── panel_are_you_sure.html
│ ├── panel_backups.html
│ ├── panel_before_head.html
│ ├── panel_dashboard.html
│ ├── panel_debug.html
│ ├── panel_debug_stat.html
│ ├── panel_debug_stat_head.html
│ ├── panel_debug_stat_head_q.html
│ ├── panel_debug_stat_q.html
│ ├── panel_debug_subhead.html
│ ├── panel_forum_edit.html
│ ├── panel_forum_edit_perms.html
│ ├── panel_forums.html
│ ├── panel_group_edit.html
│ ├── panel_group_edit_perms.html
│ ├── panel_group_edit_promotions.html
│ ├── panel_group_menu.html
│ ├── panel_groups.html
│ ├── panel_inner_menu.html
│ ├── panel_menu.html
│ ├── panel_modlogs.html
│ ├── panel_pages.html
│ ├── panel_pages_edit.html
│ ├── panel_plugins.html
│ ├── panel_reglogs.html
│ ├── panel_setting.html
│ ├── panel_settings.html
│ ├── panel_themes.html
│ ├── panel_themes_menus.html
│ ├── panel_themes_menus_item_edit.html
│ ├── panel_themes_menus_items.html
│ ├── panel_themes_widgets.html
│ ├── panel_themes_widgets_widget.html
│ ├── panel_user_edit.html
│ ├── panel_users.html
│ ├── panel_word_filters.html
│ ├── password_reset.html
│ ├── password_reset_token.html
│ ├── profile.html
│ ├── profile_comments_row.html
│ ├── profile_comments_row_alt.html
│ ├── register.html
│ ├── register_verify.html
│ ├── topic.html
│ ├── topic_alt.html
│ ├── topic_alt_inner.html
│ ├── topic_alt_mini.html
│ ├── topic_alt_poll.html
│ ├── topic_alt_posts.html
│ ├── topic_alt_quick_reply.html
│ ├── topic_alt_userinfo.html
│ ├── topic_c_attach_item.html
│ ├── topic_c_edit_post.html
│ ├── topic_c_poll_input.html
│ ├── topic_inner.html
│ ├── topic_mini.html
│ ├── topic_poll.html
│ ├── topic_posts.html
│ ├── topics.html
│ ├── topics_inner.html
│ ├── topics_mini.html
│ ├── topics_mod_floater.html
│ ├── topics_quick_topic.html
│ ├── topics_topic.html
│ ├── widget_about.html
│ ├── widget_menu.html
│ ├── widget_online.html
│ ├── widget_search_and_filter.html
│ └── widget_simple.html
├── themes/
│ ├── cosora/
│ │ ├── public/
│ │ │ ├── account.css
│ │ │ ├── convo.css
│ │ │ ├── main.css
│ │ │ ├── misc.js
│ │ │ ├── panel.css
│ │ │ └── profile.css
│ │ └── theme.json
│ ├── nox/
│ │ ├── overrides/
│ │ │ ├── login.html
│ │ │ ├── panel_before_head.html
│ │ │ ├── panel_group_menu.html
│ │ │ ├── panel_inner_menu.html
│ │ │ ├── panel_menu.html
│ │ │ ├── profile_comments_row.html
│ │ │ └── topics_topic.html
│ │ ├── public/
│ │ │ ├── acc_panel_common.css
│ │ │ ├── account.css
│ │ │ ├── convo.css
│ │ │ ├── fa-svg/
│ │ │ │ ├── LICENSE.txt
│ │ │ │ └── README.md
│ │ │ ├── main.css
│ │ │ ├── misc.js
│ │ │ ├── panel.css
│ │ │ └── profile.css
│ │ └── theme.json
│ ├── shadow/
│ │ ├── DEVELOPERS.md
│ │ ├── overrides/
│ │ │ └── login.html
│ │ ├── public/
│ │ │ ├── account.css
│ │ │ ├── convo.css
│ │ │ ├── main.css
│ │ │ ├── misc.js
│ │ │ ├── panel.css
│ │ │ └── profile.css
│ │ └── theme.json
│ └── tempra_simple/
│ ├── DEVELOPERS.md
│ ├── overrides/
│ │ └── login.html
│ ├── public/
│ │ ├── account.css
│ │ ├── convo.css
│ │ ├── main.css
│ │ ├── media.partial.css
│ │ ├── misc.js
│ │ ├── panel.css
│ │ ├── profile.css
│ │ └── sample.css
│ └── theme.json
├── tickloop.go
├── tmp/
│ └── filler.txt
├── tmpl_client/
│ └── stub.go
├── tmplstub.go
├── update-deps-linux
├── update-deps.bat
├── updater/
│ └── main.go
├── uploads/
│ └── filler.txt
└── uutils/
└── utils.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .codebeatignore
================================================
/public/chartist/**
/public/trumbowyg/**
/public/jquery-emojiarea/**
/public/font-awesome-4.7.0/**
/public/jquery-3.1.1.min.js
/public/EQCSS.min.js
/public/EQCSS.js
/schema/**
tmpl_list.go
tmpl_forum.go
tmpl_forums.go
tmpl_topic.go
tmpl_topic_alt.go
tmpl_topics.go
tmpl_profile.go
gen_mysql.go
gen_mssql.go
gen_pgsql.go
gen_router.go
================================================
FILE: .codeclimate.yml
================================================
exclude_patterns:
- "gen_*"
- "schema/*"
- "public/chartist/*"
- "public/trumbowyg/*"
- "public/jquery-emojiarea/*"
- "public/font-awesome-4.7.0/*"
- "public/jquery-3.1.1.min.js"
- "public/EQCSS.min.js"
- "public/EQCSS.js"
- "public/Sortable-1.4.0/*"
================================================
FILE: .eslintrc.json
================================================
{
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": false
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"rules": {
"no-const-assign": "warn",
"no-this-before-super": "warn",
"no-undef": "warn",
"no-unreachable": "warn",
"no-unused-vars": "warn",
"constructor-super": "warn",
"valid-typeof": "warn"
},
"globals": {
"$": true,
"addHook": true,
"runHook": true,
"addInitHook": true,
"runInitHook": true,
"loadScript": true
}
}
================================================
FILE: .gitignore
================================================
tmp/*
!tmp/filler.txt
tmp2/*
cert_test/*
tmp.txt
run_notemplategen.bat
brun.bat
attachs/*
!attachs/filler.txt
uploads/avatar_*
uploads/socialgroup_*
backups/*.sql
logs/*.log
config/config.json
node_modules/*
samples/vue/node_modules/*
samples/vue/*
bin/*
out/*
*.exe
*.exe~
*.prof
*.log
.DS_Store
.vscode/launch.json
config/config.go
QueryGen
RouterGen
Patcher
Gosora
Installer
tmpl_*.go
tmpl_*.jgo
================================================
FILE: .htaccess
================================================
# Gosora doesn't use Apache, this file is just here to stop Apache from blindly serving our config files, etc. when this program isn't intended to be served in such a manner at all
deny from all
================================================
FILE: .travis.yml
================================================
language: go
go:
- "1.13"
- "1.14"
- "1.15"
- "1.16"
- master
before_install:
- cd $HOME
- git clone https://github.com/Azareal/Gosora gosora
- cd gosora
- chmod -R 0777 .
- mv ./config/config_example.json ./config/config.json
- ./update-deps-linux
- ./dev-update-travis
- mv ./experimental/plugin_sendmail.go ..
install: true
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script: ./run-linux-tests
after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
addons:
mariadb: '10.3'
================================================
FILE: .vscode/settings.json
================================================
// Place your settings in this file to overwrite default and user settings.
{
"editor.insertSpaces": false
}
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
First and foremost, if you want to add a contribution, you'll have to open a pull request and to sign the CLA (contributor level agreement).
It's mainly there to deal with any legal issues which may come our way and to switch licenses without having to track down every contributor who has ever contributed.
Some things we could do is commercial licensing for companies which are not authorised to use open source licenses or moving to a more permissive license, although I'm not too experianced in these matters, if anyone has any ideas, then feel free to put them forward.
Try to prefix commits which introduce a lot of bugs or otherwise has a large impact on the usability of Gosora with UNSTABLE.
If something seems to be strange, then feel free to bring up an alternative for it, although I'd rather not get hung up on the little details, if it's something which is purely a matter of opinion.
# Coding Standards
All code must be unit tested where ever possible with the exception of JavaScript which is untestable with our current technologies, tread with caution there.
Use tabs not spaces for indentation.
# Golang
Use the standard linter and listen to what it tells you to do.
The route assignments in main.go are *legacy code*, add new routes to `router_gen/routes.go` instead.
Try to use the single responsibility principle where ever possible, with the exception for if doing so will cause a large performance drop. In other words, don't give your interfaces / structs too many responsibilities, keep them simple.
Avoid hand-rolling queries. Use the builders, a ready built statement or a datastore structure instead. Preferably a datastore.
Commits which require the patcher / update script to be run should be prefixed with "Database Changes: "
More coming up.
# JavaScript
Use semicolons at the end of statements. If you don't, you might wind up breaking a minifier or two.
Always use strict mode.
Don't worry about ES5, we're targetting modern browsers. If we decide to backport code to older browsers, then we'll transpile the files.
Please don't use await. It incurs too much of a cognitive overhead as to where and when you can use it. We can't use it everywhere quite yet, which means that we really should be using it nowhere.
Please don't abuse `const` just to shave off a few nanoseconds. Even in the Go server where I care about performance the most, I don't use const everywhere, only in about five spots in thirty thousand lines and I don't use it for performance at all there.
To keep consistency with Go code, variables must be camelCase.
# JSON
To keep consistency with Go code, map keys must be camelCase.
# Phrases
Try to keep the name of the phrase close to the actual phrase in english to make it easier for localisers to reason about which phrase is which.
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
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 <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
================================================
FILE: README.md
================================================
# Gosora  [](https://discord.gg/eyYvtTf)
A super fast forum software written in Go. You can talk to us on our Discord chat!
The initial code-base was forked from one of my side projects, but has now gone far beyond that. We've moved along in a development and the software should be somewhat stable for general use.
Features may break from time to time, however I will generally try to warn of the biggest offenders in advance, so that you can tread with caution around certain commits, the upcoming v0.1 will undergo even more rigorous testing.
File an issue or open a topic on the forum, if there's something you want and you very well might find it landing in the software fairly quickly.
For plugin and theme developers, things are a little dicier, as the internal APIs and ways of writing themes are in constant flux, however some stability in that area should be coming fairly soon.
If you like this software, please give it a star and give us some feedback :)
If you dislike it, please give us some feedback on how to make it better! We're always looking for feedback. We love hearing your opinions. If there's something missing or something doesn't look quite right, don't worry! We plan to add many, many things in the run up to v0.1!
# Features
Standard Forum Functionality. All of the little things you would expect of any forum software. E.g. Common Moderation features, modlogs, theme system, avatars, bbcode parser, markdown parser, report system, per-forum permissions, group permissions and so on.
Custom Pages. There are some rough edges
Emojis. Allow your users to express themselves without resorting to serving tons upon tons of image files.
In-memory static file, forum and group caches. We have a slightly more dynamic cache for users and topics.
A profile system, including profile comments and moderation tools for the profile owner.
A template engine which compiles templates down to machine code. Over forty times faster than the standard template library `html/templates`, although it does remove some of the hand holding to achieve this. Compatible with templates written for `html/templates`, so you don't need to learn any new templating language.
A plugin system. We have a number of APIs and hooks for plugins, however they're currently subject to change and don't cover as much of the software as we'd like yet.
A responsive design. Looks great on mobile phones, tablets, laptops, desktops and more!
Other modern features like alerts, likes, advanced dashboard with live stats (CPU, RAM, online user count, and so on), etc.
# Requirements
Go 1.13 or newer - You will need to install this. Pick the .msi, if you want everything sorted out for you rather than having to go around updating the environment settings. https://golang.org/doc/install
For Ubuntu, you can consult: https://tecadmin.net/install-go-on-ubuntu/
You will also want to run `ln -s /usr/local/go/bin/go` (replace /usr/local with where ever you put Go), so that go becomes visible to other users.
If you followed the instructions above, you can update to the latest version of Go simply by deleting the `/go/` folder and replacing it with a `/go/` folder for the latest version of Go.
Git - You may need this for downloading updates via the updater. You might already have this installed on your server, if the `git` commands don't work, then install this. https://git-scm.com/downloads
MySQL Database - You will need to setup a MySQL Database somewhere. A MariaDB Database works equally well and is much faster than MySQL. You could use something like WNMP / XAMPP which have a little PHP script called PhpMyAdmin for managing MySQL databases or you could install MariaDB directly.
Download the .msi installer from [MariaDB](https://mariadb.com/downloads) and run that. You may want to set it up as a service to avoid running it every-time the computer starts up.
Instructions on how to set MariaDB up on Linux: https://downloads.mariadb.org/mariadb/repositories/
We recommend changing the root password (that is the password for the user 'root'). Remember that password, you will need it for the installation process. Of course, we would advise using a user other than root for maximum security, although that adds additional steps to the process of getting everything setup.
You might also want to run `mysql_secure_installation` to further harden (aka make it more secure) MySQL / MariaDB.
If you're using Ubuntu, you might want to look at: https://www.itzgeek.com/how-tos/linux/ubuntu-how-tos/install-mariadb-on-ubuntu-16-04.html
It's entirely possible that your host already has MySQL installed and ready to go, so you might be able to skip this step, particularly if it's a managed VPS or a shared host. Or they might have a quicker and easier method of setting up MySQL.
# How to download
For Linux, you can skip down to the Installation section as it covers this.
On Windows, you might want to try the [GosoraBootstrapper](https://github.com/Azareal/GosoraBootstrapper), if you can't find the command prompt or otherwise can't follow those instructions. It's just a matter of double-clicking on the bat file there and it'll download the rest of the files for you.
# Installation
Consult [installation](https://github.com/Azareal/Gosora/blob/master/docs/installation.md) for instructions on how to install Gosora.
# Updating
Consult [updating](https://github.com/Azareal/Gosora/blob/master/docs/updating.md) for instructions on how to update Gosora.
# Running the program
*Linux*
If you have setup a service, you can run:
`./pre-run-linux`
`service gosora start`
You can then, check Gosora's current status (to see if it started up properly) with:
`service gosora status`
And you can stop it with:
`service gosora stop`
If you haven't setup a service, you can run `./run-linux`, although you will be responsible for finding a way to run it in the background, so that it doesn't close when the terminal does.
One method might be to use: https://serverfault.com/questions/34750/is-it-possible-to-detach-a-process-from-its-terminal-or-i-should-have-used-s
*Windows*
Run `run.bat`, e.g. double-clicking on it.
# How do I install plugins?
For the default plugins like Markdown and Helloworld, you can find them in the Plugin Manager of your Control Panel. For ones which aren't included by default, you will need to drop them down in the `/extend/` directory.
You will then need to recompile Gosora in order to link the plugin code with Gosora's code. For plugins not written in Go (e.g. JavaScript), they will automatically show up in your Control Panel ready to be installed, although we currently don't support these types of plugins at this time.
There are also some experimental plugins in the `/experimental/` folder like plugin_sendmail which you may want to make use of, although there aren't any particular guarantees about whether they will continue to function or not.
We're currently in the process of moving plugins from the `/` to the `/extend/` folder, if there is a piece of functionality that you would like to tap into, but which you cannot from that package, then feel free to poke me, otherwise you may need to drop it in `/` and name the package accordingly.
# Images






More images in the /images/ folder. Beware though, some of them are *really* outdated. Also, keep in mind that a new theme is in the works.
# Dependencies
These are the libraries and pieces of software which Gosora relies on to function, an "ingredients" list so to speak.
A few of these like Rez aren't currently in use, but are things we think we'll need in the very near future and want to have those things ready, so that we can quickly slot them in.
* Go 1.11+
* MariaDB (or any other MySQL compatible database engine). We'll allow other database engines in the future.
* github.com/go-sql-driver/mysql For interfacing with MariaDB.
* golang.org/x/crypto/bcrypt For hashing passwords.
* golang.org/x/crypto/argon2 For hashing passwords.
* github.com/Azareal/gopsutil For pulling information on CPU and memory usage. I've temporarily forked this, as we were having stability issues with the latest build.
* github.com/StackExchange/wmi Dependency for gopsutil on Windows.
* golang.org/x/sys/windows Also a dependency for gopsutil on Windows. This isn't needed at the moment, as I've rolled things back to an older more stable build.
* github.com/gorilla/websocket Needed for Gosora's Optional WebSockets Module.
* github.com/robertkrimen/otto Needed for the upcoming JS plugin type.
* gopkg.in/sourcemap.v1 Dependency for Otto.
* github.com/lib/pq For interfacing with PostgreSQL. You will be able to pick this instead of MariaDB soon.
* ithub.com/denisenkom/go-mssqldb For interfacing with MSSQL. You will be able to pick this instead of MSSQL soon.
* github.com/bamiaux/rez An image resizer (e.g. for spitting out thumbnails)
* github.com/esimov/caire The other image resizer, slower but may be useful for covering cases Rez does not. A third faster one we might point to at some point is probably Discord's Lilliput, however it requires a C Compiler and we don't want to add that as a dependency at this time.
* github.com/fsnotify/fsnotify A library for watching events on the file system.
* github.com/pkg/errors Some helpers to make it easier for us to track down bugs.
* More items to come here, our dependencies are going through a lot of changes, and I'll be documenting those soon ;)
# Bundled Plugins
There are several plugins which are bundled with the software by default. These cover various common tasks which aren't common enough to clutter the core with or which have competing implementation methods (E.g. plugin_markdown vs plugin_bbcode for post mark-up).
* Hey There / Skeleton / Hey There (JS Version) - Example plugins for helping you learn how to develop plugins.
* BBCode - A plugin in early development for converting BBCode Tags into HTML.
* Markdown - An extremely simple plugin for converting Markdown into HTML.
* Social Groups - An extremely unstable WIP plugin which lets users create their own little discussion areas which they can administrate / moderate on their own.
# Developers
There are a few things you'll need to know before running the more developer oriented features like the tests or the benchmarks.
The benchmarks are currently being rewritten as they're currently extremely serial which can lead to severe slow-downs when run on a home computer due to the benchmarks being run on the one core everything else is being run on (Browser, OS, etc.) and the tests not taking parallelism into account.
================================================
FILE: TODO.md
================================================
# TO-DO
Oh my, you caught me right at the start of this project. There's nothing to see here yet, asides from the absolute basics. You might want to look again later!
The various little features which somehow got stuck in the net. Don't worry, I'll get to them!
More moderation features. E.g. Move, Approval Queue (Posts made by users in certain usergroups will need to be approved by a moderator before they're publically visible), etc.
Add a simple anti-spam measure. I have quite a few ideas in mind, but it'll take a while to implement the more advanced ones, so I'd like to put off some of those to a later date and focus on the basics. E.g. CAPTCHAs, hidden fields, etc.
Add more granular permissions management to the Forum Manager.
Add a *better* plugin system. E.g. Allow for plugins written in Javascript and ones written in Go. Also, we need to add many, many, many more plugin hooks.
I will need to ponder over implementing an even faster router. We don't need one immediately, although it would be nice if we could get one in the near future. It really depends. Ideally, it would be one which can easily integrate with the current structure without much work, although I'm not beyond making some alterations to faciliate it, assuming that we don't get too tightly bound to that specific router.
Allow themes to define their own templates and to override core templates with their own.
Add a friend system.
Improve profile customisability.
Implement all the common BBCode tags in plugin_bbcode
Implement all the common Markdown codes in plugin_markdown
Add more administration features.
Add more features for improving user engagement. E.g. A like system. I have a few of these in mind, but I've been pre-occupied with implementing other features.
Add a widget system.
Add support for multi-factor authentication.
Add support for secondary emails for users.
Improve the shell scripts and possibly add support for Make? A make.go might be a good solution?
================================================
FILE: attachs/filler.txt
================================================
This file is here so that Git will include this folder in the repository.
================================================
FILE: backups/filler.txt
================================================
This file is here so that Git will include this folder in the repository.
================================================
FILE: build-linux
================================================
echo "Deleting artifacts from previous builds"
rm -f template_*.go
rm -f tmpl_*.go
rm -f gen_*.go
rm -f tmpl_client/template_*
rm -f tmpl_client/tmpl_*
rm -f ./Gosora
rm -f ./common/gen_extend.go
echo "Building the router generator"
go build -ldflags="-s -w" -o RouterGen "./router_gen"
echo "Running the router generator"
./RouterGen
echo "Building the hook stub generator"
go build -ldflags="-s -w" -o HookStubGen "./cmd/hook_stub_gen"
echo "Running the hook stub generator"
./HookStubGen
echo "Building the hook generator"
go build -tags hookgen -ldflags="-s -w" -o HookGen "./cmd/hook_gen"
echo "Running the hook generator"
./HookGen
echo "Generating the JSON handlers"
easyjson -pkg common
echo "Building the query generator"
go build -ldflags="-s -w" -o QueryGen "./cmd/query_gen"
echo "Running the query generator"
./QueryGen
echo "Building Gosora"
go generate
go build -ldflags="-s -w" -o Gosora
echo "Building the installer"
go build -ldflags="-s -w" -o Installer "./install"
================================================
FILE: build-linux-nowebsockets
================================================
echo "Deleting artifacts from previous builds"
rm -f template_*.go
rm -f tmpl_*.go
rm -f gen_*.go
rm -f tmpl_client/template_*
rm -f tmpl_client/tmpl_*
rm -f ./Gosora
rm -f ./common/gen_extend.go
echo "Building the router generator"
go build -ldflags="-s -w" -o RouterGen "./router_gen"
echo "Running the router generator"
./RouterGen
echo "Building the hook stub generator"
go build -ldflags="-s -w" -o HookStubGen "./cmd/hook_stub_gen"
echo "Running the hook stub generator"
./HookStubGen
echo "Building the hook generator"
go build -tags hookgen -ldflags="-s -w" -o HookGen "./cmd/hook_gen"
echo "Running the hook generator"
./HookGen
echo "Generating the JSON handlers"
easyjson -pkg common
echo "Building the query generator"
go build -ldflags="-s -w" -o QueryGen "./cmd/query_gen"
echo "Running the query generator"
./QueryGen
echo "Building Gosora"
go generate
go build -ldflags="-s -w" -o Gosora -tags no_ws
echo "Building the installer"
go build -ldflags="-s -w" -o Installer "./install"
================================================
FILE: build-nowebsockets.bat
================================================
@echo off
rem TODO: Make these deletes a little less noisy
del "template_*.go"
del "tmpl_*.go"
del "gen_*.go"
del ".\tmpl_client\template_*"
del ".\tmpl_client\tmpl_*"
del ".\common\gen_extend.go"
del "gosora.exe"
echo Generating the dynamic code
go generate
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Generating the JSON handlers
easyjson -pkg common
echo Building the executable
go build -ldflags="-s -w" -o gosora.exe -tags no_ws
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the installer
go build -ldflags="-s -w" "./cmd/install"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the router generator
go build -ldflags="-s -w" ./router_gen
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the hook stub generator
go build -ldflags="-s -w" "./cmd/hook_stub_gen"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the hook generator
go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build -ldflags="-s -w" "./cmd/query_gen"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Gosora was successfully built
pause
================================================
FILE: build.bat
================================================
@echo off
rem TODO: Make these deletes a little less noisy
del "template_*.go"
del "tmpl_*.go"
del "gen_*.go"
del ".\tmpl_client\template_*"
del ".\tmpl_client\tmpl_*"
del ".\common\gen_extend.go"
del "gosora.exe"
echo Generating the dynamic code
go generate
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Generating the JSON handlers
easyjson -pkg common
echo Building the executable
go build -ldflags="-s -w" -o gosora.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the installer
go build -ldflags="-s -w" "./cmd/install"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the router generator
go build -ldflags="-s -w" ./router_gen
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the hook stub generator
go build -ldflags="-s -w" "./cmd/hook_stub_gen"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the hook generator
go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build -ldflags="-s -w" "./cmd/query_gen"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Gosora was successfully built
pause
================================================
FILE: build_templates.bat
================================================
echo Building the templates
gosora.exe -build-templates
echo Rebuilding the executable
go build -ldflags="-s -w" -o gosora.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
pause
================================================
FILE: cmd/common_hook_gen/hookgen.go
================================================
package hookgen
import (
"bytes"
"log"
"os"
"text/template"
)
type HookVars struct {
Imports []string
Hooks []Hook
}
type Hook struct {
Name string
Params string
Params2 string
Ret string
Type string
Any bool
MultiHook bool
Skip bool
DefaultRet string
Pure string
}
func AddHooks(add func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string)) {
vhookskip := func(name, params string) {
add(name, params, "(bool,RouteError)", "VhookSkippable_", false, true, "false,nil", "")
}
vhookskip("simple_forum_check_pre_perms", "w http.ResponseWriter,r *http.Request,u *User,fid *int,h *HeaderLite")
vhookskip("forum_check_pre_perms", "w http.ResponseWriter,r *http.Request,u *User,fid *int,h *Header")
vhookskip("router_after_filters", "w http.ResponseWriter,r *http.Request,prefix string")
vhookskip("router_pre_route", "w http.ResponseWriter,r *http.Request,u *User,prefix string")
vhookskip("route_forum_list_start", "w http.ResponseWriter,r *http.Request,u *User,h *Header")
vhookskip("route_topic_list_start", "w http.ResponseWriter,r *http.Request,u *User,h *Header")
vhookskip("route_attach_start", "w http.ResponseWriter,r *http.Request,u *User,fname string")
vhookskip("route_attach_post_get", "w http.ResponseWriter,r *http.Request,u *User,a *Attachment")
vhooknoret := func(name, params string) {
add(name, params, "", "Vhooks", false, false, "false,nil", "")
}
vhooknoret("router_end", "w http.ResponseWriter,r *http.Request,u *User,prefix string,extraData string")
vhooknoret("topic_reply_row_assign", "r *ReplyUser")
vhooknoret("counters_perf_tick_row", "low int64,high int64,avg int64")
//forums_frow_assign
//Hook(name string, data interface{}) interface{}
/*hook := func(name, params, ret, pure string) {
add(name,params,ret,"Hooks",true,false,ret,pure)
}*/
hooknoret := func(name, params string) {
add(name, params, "", "HooksNoRet", true, false, "", "")
}
hooknoret("forums_frow_assign", "f *Forum")
hookskip := func(name, params string) {
add(name, params, "(skip bool)", "HooksSkip", true, true, "", "")
}
//hookskip("forums_frow_assign","f *Forum")
hookskip("topic_create_frow_assign", "f *Forum")
hookss := func(name string) {
add(name, "d string", "string", "Sshooks", true, false, "", "d")
}
hookss("topic_ogdesc_assign")
}
func Write(hookVars HookVars) {
fileData := `// Code generated by Gosora's Hook Generator. DO NOT EDIT.
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package common
import ({{range .Imports}}
"{{.}}"{{end}}
)
{{range .Hooks}}
func H_{{.Name}}_hook(t *HookTable,{{.Params}}) {{.Ret}} { {{if .Any}}
{{if .MultiHook}}for _, hook := range t.{{.Type}}["{{.Name}}"] {
{{if .Skip}}if skip = hook({{.Params2}}); skip {
break
}{{else}}{{if .Pure}}{{.Pure}} = {{else if .Ret}}return {{end}}hook({{.Params2}}){{end}}
}{{else}}hook := t.{{.Type}}["{{.Name}}"]
if hook != nil {
{{if .Ret}}return {{end}}hook({{.Params2}})
} {{end}}{{end}}{{if .Pure}}
return {{.Pure}}{{else if .Ret}}
return {{.DefaultRet}}{{end}}
}{{end}}
`
tmpl := template.Must(template.New("hooks").Parse(fileData))
var b bytes.Buffer
if e := tmpl.Execute(&b, hookVars); e != nil {
log.Fatal(e)
}
err := writeFile("./common/gen_extend.go", b.String())
if err != nil {
log.Fatal(err)
}
}
func writeFile(name, body string) error {
f, e := os.Create(name)
if e != nil {
return e
}
if _, e = f.WriteString(body); e != nil {
return e
}
if e = f.Sync(); e != nil {
return e
}
return f.Close()
}
================================================
FILE: cmd/elasticsearch/setup.go
================================================
// Work in progress
package main
import (
"context"
"database/sql"
"encoding/json"
"errors"
"log"
"os"
"strconv"
c "github.com/Azareal/Gosora/common"
"github.com/Azareal/Gosora/query_gen"
"gopkg.in/olivere/elastic.v6"
)
func main() {
log.Print("Loading the configuration data")
err := c.LoadConfig()
if err != nil {
log.Fatal(err)
}
log.Print("Processing configuration data")
err = c.ProcessConfig()
if err != nil {
log.Fatal(err)
}
if c.DbConfig.Adapter != "mysql" && c.DbConfig.Adapter != "" {
log.Fatal("Only MySQL is supported for upgrades right now, please wait for a newer build of the patcher")
}
err = prepMySQL()
if err != nil {
log.Fatal(err)
}
client, err := elastic.NewClient(elastic.SetErrorLog(log.New(os.Stdout, "ES ", log.LstdFlags)))
if err != nil {
log.Fatal(err)
}
_, _, err = client.Ping("http://127.0.0.1:9200").Do(context.Background())
if err != nil {
log.Fatal(err)
}
err = setupIndices(client)
if err != nil {
log.Fatal(err)
}
err = setupData(client)
if err != nil {
log.Fatal(err)
}
}
func prepMySQL() error {
return qgen.Builder.Init("mysql", map[string]string{
"host": c.DbConfig.Host,
"port": c.DbConfig.Port,
"name": c.DbConfig.Dbname,
"username": c.DbConfig.Username,
"password": c.DbConfig.Password,
"collation": "utf8mb4_general_ci",
})
}
type ESIndexBase struct {
Mappings ESIndexMappings `json:"mappings"`
}
type ESIndexMappings struct {
Doc ESIndexDoc `json:"_doc"`
}
type ESIndexDoc struct {
Properties map[string]map[string]string `json:"properties"`
}
type ESDocMap map[string]map[string]string
func (d ESDocMap) Add(column string, cType string) {
d["column"] = map[string]string{"type": cType}
}
func setupIndices(client *elastic.Client) error {
exists, err := client.IndexExists("topics").Do(context.Background())
if err != nil {
return err
}
if exists {
deleteIndex, err := client.DeleteIndex("topics").Do(context.Background())
if err != nil {
return err
}
if !deleteIndex.Acknowledged {
return errors.New("delete not acknowledged")
}
}
docMap := make(ESDocMap)
docMap.Add("tid", "integer")
docMap.Add("title", "text")
docMap.Add("content", "text")
docMap.Add("createdBy", "integer")
docMap.Add("ip", "ip")
docMap.Add("suggest", "completion")
indexBase := ESIndexBase{ESIndexMappings{ESIndexDoc{docMap}}}
oBytes, err := json.Marshal(indexBase)
if err != nil {
return err
}
createIndex, err := client.CreateIndex("topics").Body(string(oBytes)).Do(context.Background())
if err != nil {
return err
}
if !createIndex.Acknowledged {
return errors.New("not acknowledged")
}
exists, err = client.IndexExists("replies").Do(context.Background())
if err != nil {
return err
}
if exists {
deleteIndex, err := client.DeleteIndex("replies").Do(context.Background())
if err != nil {
return err
}
if !deleteIndex.Acknowledged {
return errors.New("delete not acknowledged")
}
}
docMap = make(ESDocMap)
docMap.Add("rid", "integer")
docMap.Add("tid", "integer")
docMap.Add("content", "text")
docMap.Add("createdBy", "integer")
docMap.Add("ip", "ip")
docMap.Add("suggest", "completion")
indexBase = ESIndexBase{ESIndexMappings{ESIndexDoc{docMap}}}
oBytes, err = json.Marshal(indexBase)
if err != nil {
return err
}
createIndex, err = client.CreateIndex("replies").Body(string(oBytes)).Do(context.Background())
if err != nil {
return err
}
if !createIndex.Acknowledged {
return errors.New("not acknowledged")
}
return nil
}
type ESTopic struct {
ID int `json:"tid"`
Title string `json:"title"`
Content string `json:"content"`
CreatedBy int `json:"createdBy"`
IP string `json:"ip"`
}
type ESReply struct {
ID int `json:"rid"`
TID int `json:"tid"`
Content string `json:"content"`
CreatedBy int `json:"createdBy"`
IP string `json:"ip"`
}
func setupData(client *elastic.Client) error {
tcount := 4
errs := make(chan error)
go func() {
tin := make([]chan ESTopic, tcount)
tf := func(tin chan ESTopic) {
for {
topic, more := <-tin
if !more {
break
}
_, err := client.Index().Index("topics").Type("_doc").Id(strconv.Itoa(topic.ID)).BodyJson(topic).Do(context.Background())
if err != nil {
errs <- err
}
}
}
for i := 0; i < 4; i++ {
go tf(tin[i])
}
oi := 0
err := qgen.NewAcc().Select("topics").Cols("tid,title,content,createdBy,ip").Each(func(rows *sql.Rows) error {
t := ESTopic{}
err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IP)
if err != nil {
return err
}
tin[oi] <- t
if oi < 3 {
oi++
}
return nil
})
for i := 0; i < 4; i++ {
close(tin[i])
}
errs <- err
}()
go func() {
rin := make([]chan ESReply, tcount)
rf := func(rin chan ESReply) {
for {
reply, more := <-rin
if !more {
break
}
_, err := client.Index().Index("replies").Type("_doc").Id(strconv.Itoa(reply.ID)).BodyJson(reply).Do(context.Background())
if err != nil {
errs <- err
}
}
}
for i := 0; i < 4; i++ {
rf(rin[i])
}
oi := 0
err := qgen.NewAcc().Select("replies").Cols("rid,tid,content,createdBy,ip").Each(func(rows *sql.Rows) error {
r := ESReply{}
err := rows.Scan(&r.ID, &r.TID, &r.Content, &r.CreatedBy, &r.IP)
if err != nil {
return err
}
rin[oi] <- r
if oi < 3 {
oi++
}
return nil
})
for i := 0; i < 4; i++ {
close(rin[i])
}
errs <- err
}()
fin := 0
for {
err := <-errs
if err == nil {
fin++
if fin == 2 {
return nil
}
} else {
return err
}
}
}
================================================
FILE: cmd/hook_gen/main.go
================================================
// +build hookgen
package main // import "github.com/Azareal/Gosora/hook_gen"
import (
"fmt"
"log"
"runtime/debug"
"strings"
h "github.com/Azareal/Gosora/cmd/common_hook_gen"
c "github.com/Azareal/Gosora/common"
_ "github.com/Azareal/Gosora/extend"
)
// TODO: Make sure all the errors in this file propagate upwards properly
func main() {
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
debug.PrintStack()
}
}()
hooks := make(map[string]int)
for _, pl := range c.Plugins {
if len(pl.Meta.Hooks) > 0 {
for _, hook := range pl.Meta.Hooks {
hooks[hook]++
}
continue
}
if pl.Init != nil {
if e := pl.Init(pl); e != nil {
log.Print("early plugin init err: ", e)
return
}
}
if pl.Hooks != nil {
log.Print("Hooks not nil for ", pl.UName)
for hook, _ := range pl.Hooks {
hooks[hook] += 1
}
}
}
log.Printf("hooks: %+v\n", hooks)
imports := []string{"net/http"}
hookVars := h.HookVars{imports, nil}
var params2sb strings.Builder
add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) {
first := true
for _, param := range strings.Split(params, ",") {
if !first {
params2sb.WriteRune(',')
}
pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param), " ", " "), " ")
params2sb.WriteString(pspl[0])
first = false
}
hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2sb.String(), ret, htype, hooks[name] > 0, multiHook, skip, defaultRet, pure})
params2sb.Reset()
}
h.AddHooks(add)
h.Write(hookVars)
log.Println("Successfully generated the hooks")
}
================================================
FILE: cmd/hook_stub_gen/main.go
================================================
package main // import "github.com/Azareal/Gosora/hook_stub_gen"
import (
"fmt"
"log"
"strings"
"runtime/debug"
h "github.com/Azareal/Gosora/cmd/common_hook_gen"
)
// TODO: Make sure all the errors in this file propagate upwards properly
func main() {
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
debug.PrintStack()
}
}()
imports := []string{"net/http"}
hookVars := h.HookVars{imports,nil}
add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) {
var params2 string
first := true
for _, param := range strings.Split(params,",") {
if !first {
params2 += ","
}
pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param)," "," ")," ")
params2 += pspl[0]
first = false
}
hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, true, multiHook, skip, defaultRet,pure})
}
h.AddHooks(add)
h.Write(hookVars)
log.Println("Successfully generated the hooks")
}
================================================
FILE: cmd/install/install.go
================================================
/*
*
* Gosora Installer
* Copyright Azareal 2017 - 2019
*
*/
package main
import (
"bufio"
"errors"
"fmt"
"os"
"runtime/debug"
"strconv"
"strings"
"github.com/Azareal/Gosora/install"
)
var scanner *bufio.Scanner
var siteShortName string
var siteName string
var siteURL string
var serverPort string
var defaultAdapter = "mysql"
var defaultHost = "localhost"
var defaultUsername = "root"
var defaultDbname = "gosora"
var defaultSiteShortName = "SN"
var defaultSiteName = "Site Name"
var defaultsiteURL = "localhost"
var defaultServerPort = "80" // 8080's a good one, if you're testing and don't want it to clash with port 80
func main() {
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
debug.PrintStack()
pressAnyKey()
return
}
}()
scanner = bufio.NewScanner(os.Stdin)
fmt.Println("Welcome to Gosora's Installer")
fmt.Println("We're going to take you through a few steps to help you get started :)")
adap, ok := handleDatabaseDetails()
if !ok {
err := scanner.Err()
if err != nil {
fmt.Println(err)
} else {
err = errors.New("Something went wrong!")
}
abortError(err)
return
}
if !getSiteDetails() {
err := scanner.Err()
if err != nil {
fmt.Println(err)
} else {
err = errors.New("Something went wrong!")
}
abortError(err)
return
}
err := adap.InitDatabase()
if err != nil {
abortError(err)
return
}
err = adap.TableDefs()
if err != nil {
abortError(err)
return
}
err = adap.CreateAdmin()
if err != nil {
abortError(err)
return
}
err = adap.InitialData()
if err != nil {
abortError(err)
return
}
configContents := []byte(`{
"Site": {
"ShortName":"` + siteShortName + `",
"Name":"` + siteName + `",
"URL":"` + siteURL + `",
"Port":"` + serverPort + `",
"EnableSsl":false,
"EnableEmails":false,
"HasProxy":false,
"Language": "english"
},
"Config": {
"SslPrivkey": "",
"SslFullchain": "",
"SMTPServer": "",
"SMTPUsername": "",
"SMTPPassword": "",
"SMTPPort": "25",
"MaxRequestSizeStr":"5MB",
"UserCache":"static",
"TopicCache":"static",
"ReplyCache":"static",
"UserCacheCapacity":180,
"TopicCacheCapacity":400,
"ReplyCacheCapacity":20,
"DefaultPath":"/topics/",
"DefaultGroup":3,
"ActivationGroup":5,
"StaffCSS":"staff_post",
"DefaultForum":2,
"MinifyTemplates":true,
"BuildSlugs":true,
"ServerCount":1,
"Noavatar":"https://api.adorable.io/avatars/{width}/{id}.png",
"ItemsPerPage":25
},
"Database": {
"Adapter": "` + adap.Name() + `",
"Host": "` + adap.DBHost() + `",
"Username": "` + adap.DBUsername() + `",
"Password": "` + adap.DBPassword() + `",
"Dbname": "` + adap.DBName() + `",
"Port": "` + adap.DBPort() + `",
"TestAdapter": "` + adap.Name() + `",
"TestHost": "",
"TestUsername": "",
"TestPassword": "",
"TestDbname": "",
"TestPort": ""
},
"Dev": {
"DebugMode":true,
"SuperDebug":false
}
}`)
fmt.Println("Opening the configuration file")
configFile, err := os.Create("./config/config.json")
if err != nil {
abortError(err)
return
}
fmt.Println("Writing to the configuration file...")
_, err = configFile.Write(configContents)
if err != nil {
abortError(err)
return
}
configFile.Sync()
configFile.Close()
fmt.Println("Finished writing to the configuration file")
fmt.Println("Yay, you have successfully installed Gosora!")
fmt.Println("Your name is Admin and you can login with the password 'password'. Don't forget to change it! Seriously. It's really insecure.")
pressAnyKey()
}
func abortError(err error) {
fmt.Println(err)
fmt.Println("Aborting installation...")
pressAnyKey()
}
func handleDatabaseDetails() (adap install.InstallAdapter, ok bool) {
var dbAdapter string
var dbHost string
var dbUsername string
var dbPassword string
var dbName string
// TODO: Let the admin set the database port?
//var dbPort string
for {
fmt.Println("Which database adapter do you wish to use? mysql or mssql? Default: mysql")
if !scanner.Scan() {
return nil, false
}
dbAdapter := strings.TrimSpace(scanner.Text())
if dbAdapter == "" {
dbAdapter = defaultAdapter
}
adap, ok = install.Lookup(dbAdapter)
if ok {
break
}
fmt.Println("That adapter doesn't exist")
}
fmt.Println("Set database adapter to " + dbAdapter)
fmt.Println("Database Host? Default: " + defaultHost)
if !scanner.Scan() {
return nil, false
}
dbHost = scanner.Text()
if dbHost == "" {
dbHost = defaultHost
}
fmt.Println("Set database host to " + dbHost)
fmt.Println("Database Username? Default: " + defaultUsername)
if !scanner.Scan() {
return nil, false
}
dbUsername = scanner.Text()
if dbUsername == "" {
dbUsername = defaultUsername
}
fmt.Println("Set database username to " + dbUsername)
fmt.Println("Database Password? Default: ''")
if !scanner.Scan() {
return nil, false
}
dbPassword = scanner.Text()
if len(dbPassword) == 0 {
fmt.Println("You didn't set a password for this user. This won't block the installation process, but it might create security issues in the future.")
fmt.Println("")
} else {
fmt.Println("Set password to " + obfuscatePassword(dbPassword))
}
fmt.Println("Database Name? Pick a name you like or one provided to you. Default: " + defaultDbname)
if !scanner.Scan() {
return nil, false
}
dbName = scanner.Text()
if dbName == "" {
dbName = defaultDbname
}
fmt.Println("Set database name to " + dbName)
adap.SetConfig(dbHost, dbUsername, dbPassword, dbName, adap.DefaultPort())
return adap, true
}
func getSiteDetails() bool {
fmt.Println("Okay. We also need to know some actual information about your site!")
fmt.Println("What's your site's name? Default: " + defaultSiteName)
if !scanner.Scan() {
return false
}
siteName = scanner.Text()
if siteName == "" {
siteName = defaultSiteName
}
fmt.Println("Set the site name to " + siteName)
// ? - We could compute this based on the first letter of each word in the site's name, if it's name spans multiple words. I'm not sure how to do this for single word names.
fmt.Println("Can we have a short abbreviation for your site? Default: " + defaultSiteShortName)
if !scanner.Scan() {
return false
}
siteShortName = scanner.Text()
if siteShortName == "" {
siteShortName = defaultSiteShortName
}
fmt.Println("Set the short name to " + siteShortName)
fmt.Println("What's your site's url? Default: " + defaultsiteURL)
if !scanner.Scan() {
return false
}
siteURL = scanner.Text()
if siteURL == "" {
siteURL = defaultsiteURL
}
fmt.Println("Set the site url to " + siteURL)
fmt.Println("What port do you want the server to listen on? If you don't know what this means, you should probably leave it on the default. Default: " + defaultServerPort)
if !scanner.Scan() {
return false
}
serverPort = scanner.Text()
if serverPort == "" {
serverPort = defaultServerPort
}
_, err := strconv.Atoi(serverPort)
if err != nil {
fmt.Println("That's not a valid number!")
return false
}
fmt.Println("Set the server port to " + serverPort)
return true
}
func obfuscatePassword(password string) (out string) {
for i := 0; i < len(password); i++ {
out += "*"
}
return out
}
func pressAnyKey() {
//fmt.Println("Press any key to exit...")
fmt.Println("Please press enter to exit...")
for scanner.Scan() {
_ = scanner.Text()
return
}
}
================================================
FILE: cmd/query_gen/build.bat
================================================
@echo off
echo Building the query generator
go build -ldflags="-s -w"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo The query generator was successfully built
pause
================================================
FILE: cmd/query_gen/main.go
================================================
/* WIP Under Construction */
package main // import "github.com/Azareal/Gosora/query_gen"
import (
"encoding/json"
"fmt"
"log"
"os"
"runtime/debug"
"strconv"
"strings"
c "github.com/Azareal/Gosora/common"
qgen "github.com/Azareal/Gosora/query_gen"
)
// TODO: Make sure all the errors in this file propagate upwards properly
func main() {
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
debug.PrintStack()
return
}
}()
log.Println("Running the query generator")
for _, a := range qgen.Registry {
log.Printf("Building the queries for the %s adapter", a.GetName())
qgen.Install.SetAdapterInstance(a)
qgen.Install.AddPlugins(NewPrimaryKeySpitter()) // TODO: Do we really need to fill the spitter for every adapter?
e := writeStatements(a)
if e != nil {
log.Print(e)
}
e = qgen.Install.Write()
if e != nil {
log.Print(e)
}
e = a.Write()
if e != nil {
log.Print(e)
}
}
}
// nolint
func writeStatements(a qgen.Adapter) (err error) {
e := func(f func(qgen.Adapter) error) {
if err != nil {
return
}
err = f(a)
}
e(createTables)
e(seedTables)
e(writeSelects)
e(writeLeftJoins)
e(writeInnerJoins)
e(writeInserts)
e(writeUpdates)
e(writeDeletes)
e(writeSimpleCounts)
e(writeInsertSelects)
e(writeInsertLeftJoins)
e(writeInsertInnerJoins)
return err
}
type si = map[string]interface{}
type tK = tblKey
func seedTables(a qgen.Adapter) error {
qgen.Install.AddIndex("topics", "parentID", "parentID")
qgen.Install.AddIndex("replies", "tid", "tid")
qgen.Install.AddIndex("polls", "parentID", "parentID")
qgen.Install.AddIndex("likes", "targetItem", "targetItem")
qgen.Install.AddIndex("emails", "uid", "uid")
qgen.Install.AddIndex("attachments", "originID", "originID")
qgen.Install.AddIndex("attachments", "path", "path")
qgen.Install.AddIndex("activity_stream_matches", "watcher", "watcher")
// TODO: Remove these keys to save space when Elasticsearch is active?
//qgen.Install.AddKey("topics", "title", tK{"title", "fulltext", "", false})
//qgen.Install.AddKey("topics", "content", tK{"content", "fulltext", "", false})
//qgen.Install.AddKey("topics", "title,content", tK{"title,content", "fulltext", "", false})
//qgen.Install.AddKey("replies", "content", tK{"content", "fulltext", "", false})
insert := func(tbl, cols, vals string) {
qgen.Install.SimpleInsert(tbl, cols, vals)
}
insert("sync", "last_update", "UTC_TIMESTAMP()")
addSetting := func(name, content, stype string, constraints ...string) {
if strings.Contains(name, "'") {
panic("name contains '")
}
if strings.Contains(stype, "'") {
panic("stype contains '")
}
// TODO: Add more field validators
cols := "name,content,type"
if len(constraints) > 0 {
cols += ",constraints"
}
q := func(s string) string {
return "'" + s + "'"
}
c := func() string {
if len(constraints) == 0 {
return ""
}
return "," + q(constraints[0])
}
insert("settings", cols, q(name)+","+q(content)+","+q(stype)+c())
}
addSetting("activation_type", "1", "list", "1-3")
addSetting("bigpost_min_words", "250", "int")
addSetting("megapost_min_words", "1000", "int")
addSetting("meta_desc", "", "html-attribute")
addSetting("rapid_loading", "1", "bool")
addSetting("google_site_verify", "", "html-attribute")
addSetting("avatar_visibility", "0", "list", "0-1")
insert("themes", "uname, default", "'cosora',1")
insert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation?
/*
The Permissions:
Global Permissions:
BanUsers
ActivateUsers
EditUser
EditUserEmail
EditUserPassword
EditUserGroup
EditUserGroupSuperMod
EditUserGroupAdmin
EditGroup
EditGroupLocalPerms
EditGroupGlobalPerms
EditGroupSuperMod
EditGroupAdmin
ManageForums
EditSettings
ManageThemes
ManagePlugins
ViewAdminLogs
ViewIPs
Non-staff Global Permissions:
UploadFiles
UploadAvatars
UseConvos
UseConvosOnlyWithMod
CreateProfileReply
AutoEmbed
AutoLink
// CreateConvo ?
// CreateConvoReply ?
Forum Permissions:
ViewTopic
LikeItem
CreateTopic
EditTopic
DeleteTopic
CreateReply
EditReply
DeleteReply
PinTopic
CloseTopic
MoveTopic
*/
p := func(perms *c.Perms) string {
jBytes, err := json.Marshal(perms)
if err != nil {
panic(err)
}
return string(jBytes)
}
addGroup := func(name string, perms c.Perms, mod, admin, banned bool, tag string) {
mi, ai, bi := "0", "0", "0"
if mod {
mi = "1"
}
if admin {
ai = "1"
}
if banned {
bi = "1"
}
insert("users_groups", "name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag", `'`+name+`','`+p(&perms)+`','{}',`+mi+`,`+ai+`,`+bi+`,"`+tag+`"`)
}
perms := c.AllPerms
perms.EditUserGroupAdmin = false
perms.EditGroupAdmin = false
addGroup("Administrator", perms, true, true, false, "Admin")
perms = c.Perms{BanUsers: true, ActivateUsers: true, EditUser: true, EditUserEmail: false, EditUserGroup: true, ViewIPs: true, UploadFiles: true, UploadAvatars: true, UseConvos: true, UseConvosOnlyWithMod: true, CreateProfileReply: true, AutoEmbed: true, AutoLink: true, ViewTopic: true, LikeItem: true, CreateTopic: true, EditTopic: true, DeleteTopic: true, CreateReply: true, EditReply: true, DeleteReply: true, PinTopic: true, CloseTopic: true, MoveTopic: true}
addGroup("Moderator", perms, true, false, false, "Mod")
perms = c.Perms{UploadFiles: true, UploadAvatars: true, UseConvos: true, UseConvosOnlyWithMod: true, CreateProfileReply: true, AutoEmbed: true, AutoLink: true, ViewTopic: true, LikeItem: true, CreateTopic: true, CreateReply: true}
addGroup("Member", perms, false, false, false, "")
perms = c.Perms{ViewTopic: true}
addGroup("Banned", perms, false, false, true, "")
addGroup("Awaiting Activation", c.Perms{ViewTopic: true, UseConvosOnlyWithMod: true}, false, false, false, "")
addGroup("Not Loggedin", perms, false, false, false, "Guest")
//
// TODO: Stop processFields() from stripping the spaces in the descriptions in the next commit
insert("forums", "name, active, desc, tmpl", "'Reports',0,'All the reports go here',''")
insert("forums", "name, lastTopicID, lastReplyerID, desc, tmpl", "'General',1,1,'A place for general discussions which don't fit elsewhere',''")
//
/*var addForumPerm = func(gid, fid int, permStr string) {
insert("forums_permissions", "gid, fid, permissions", strconv.Itoa(gid)+`,`+strconv.Itoa(fid)+`,'`+permStr+`'`)
}*/
insert("forums_permissions", "gid, fid, permissions", `1,1,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"PinTopic":true,"CloseTopic":true}'`)
insert("forums_permissions", "gid, fid, permissions", `2,1,'{"ViewTopic":true,"CreateReply":true,"CloseTopic":true}'`)
insert("forums_permissions", "gid, fid, permissions", "3,1,'{}'")
insert("forums_permissions", "gid, fid, permissions", "4,1,'{}'")
insert("forums_permissions", "gid, fid, permissions", "5,1,'{}'")
insert("forums_permissions", "gid, fid, permissions", "6,1,'{}'")
//
insert("forums_permissions", "gid, fid, permissions", `1,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true,"EditTopic":true,"DeleteTopic":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}'`)
insert("forums_permissions", "gid, fid, permissions", `2,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true,"EditTopic":true,"DeleteTopic":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}'`)
insert("forums_permissions", "gid, fid, permissions", `3,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true}'`)
insert("forums_permissions", "gid, fid, permissions", `4,2,'{"ViewTopic":true}'`)
insert("forums_permissions", "gid, fid, permissions", `5,2,'{"ViewTopic":true}'`)
insert("forums_permissions", "gid, fid, permissions", `6,2,'{"ViewTopic":true}'`)
//
insert("topics", "title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, createdBy, parentID, ip", "'Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,''")
insert("replies", "tid, content, parsed_content, createdAt, createdBy, lastUpdated, lastEdit, lastEditBy, ip", "1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,''")
insert("menus", "", "")
// Go maps have a random iteration order, so we have to do this, otherwise the schema files will become unstable and harder to audit
order := 0
mOrder := "mid, name, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly"
addMenuItem := func(data map[string]interface{}) {
if data["mid"] == nil {
data["mid"] = 1
}
if data["position"] == nil {
data["position"] = "left"
}
cols, values := qgen.InterfaceMapToInsertStrings(data, mOrder)
insert("menu_items", cols+", order", values+","+strconv.Itoa(order))
order++
}
addMenuItem(si{"name": "{lang.menu_forums}", "htmlID": "menu_forums", "path": "/forums/", "aria": "{lang.menu_forums_aria}", "tooltip": "{lang.menu_forums_tooltip}"})
addMenuItem(si{"name": "{lang.menu_topics}", "htmlID": "menu_topics", "cssClass": "menu_topics", "path": "/topics/", "aria": "{lang.menu_topics_aria}", "tooltip": "{lang.menu_topics_tooltip}"})
addMenuItem(si{"htmlID": "general_alerts", "cssClass": "menu_alerts", "position": "right", "tmplName": "menu_alerts"})
addMenuItem(si{"name": "{lang.menu_account}", "cssClass": "menu_account", "path": "/user/edit/", "aria": "{lang.menu_account_aria}", "tooltip": "{lang.menu_account_tooltip}", "memberOnly": true})
addMenuItem(si{"name": "{lang.menu_profile}", "cssClass": "menu_profile", "path": "{me.Link}", "aria": "{lang.menu_profile_aria}", "tooltip": "{lang.menu_profile_tooltip}", "memberOnly": true})
addMenuItem(si{"name": "{lang.menu_panel}", "cssClass": "menu_panel menu_account", "path": "/panel/", "aria": "{lang.menu_panel_aria}", "tooltip": "{lang.menu_panel_tooltip}", "memberOnly": true, "staffOnly": true})
addMenuItem(si{"name": "{lang.menu_logout}", "cssClass": "menu_logout", "path": "/accounts/logout/?s={me.Session}", "aria": "{lang.menu_logout_aria}", "tooltip": "{lang.menu_logout_tooltip}", "memberOnly": true})
addMenuItem(si{"name": "{lang.menu_register}", "cssClass": "menu_register", "path": "/accounts/create/", "aria": "{lang.menu_register_aria}", "tooltip": "{lang.menu_register_tooltip}", "guestOnly": true})
addMenuItem(si{"name": "{lang.menu_login}", "cssClass": "menu_login", "path": "/accounts/login/", "aria": "{lang.menu_login_aria}", "tooltip": "{lang.menu_login_tooltip}", "guestOnly": true})
/*var fSet []string
for _, table := range tables {
fSet = append(fSet, "'"+table+"'")
}
qgen.Install.SimpleBulkInsert("tables", "name", fSet)*/
return nil
}
// ? - What is this for?
/*func copyInsertMap(in map[string]interface{}) (out map[string]interface{}) {
out = make(map[string]interface{})
for col, value := range in {
out[col] = value
}
return out
}*/
type LitStr string
func writeSelects(a qgen.Adapter) error {
b := a.Builder()
// Looking for getTopic? Your statement is in another castle
//b.Select("isPluginInstalled").Table("plugins").Columns("installed").Where("uname = ?").Parse()
b.Select("forumEntryExists").Table("forums").Columns("fid").Where("name = ''").Orderby("fid ASC").Limit("0,1").Parse()
b.Select("groupEntryExists").Table("users_groups").Columns("gid").Where("name = ''").Orderby("gid ASC").Limit("0,1").Parse()
return nil
}
func writeLeftJoins(a qgen.Adapter) error {
a.SimpleLeftJoin("getForumTopics", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "topics.parentID = ?", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc", "")
return nil
}
func writeInnerJoins(a qgen.Adapter) (err error) {
return nil
}
func writeInserts(a qgen.Adapter) error {
b := a.Builder()
b.Insert("addForumPermsToForum").Table("forums_permissions").Columns("gid,fid,preset,permissions").Fields("?,?,?,?").Parse()
return nil
}
func writeUpdates(a qgen.Adapter) error {
b := a.Builder()
b.Update("updateEmail").Table("emails").Set("email = ?, uid = ?, validated = ?, token = ?").Where("email = ?").Parse()
b.Update("setTempGroup").Table("users").Set("temp_group = ?").Where("uid = ?").Parse()
b.Update("bumpSync").Table("sync").Set("last_update = UTC_TIMESTAMP()").Parse()
return nil
}
func writeDeletes(a qgen.Adapter) error {
b := a.Builder()
//b.Delete("deleteForumPermsByForum").Table("forums_permissions").Where("fid=?").Parse()
b.Delete("deleteActivityStreamMatch").Table("activity_stream_matches").Where("watcher=? AND asid=?").Parse()
//b.Delete("deleteActivityStreamMatchesByWatcher").Table("activity_stream_matches").Where("watcher=?").Parse()
return nil
}
func writeSimpleCounts(a qgen.Adapter) error {
return nil
}
func writeInsertSelects(a qgen.Adapter) error {
/*a.SimpleInsertSelect("addForumPermsToForumAdmins",
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
)*/
/*a.SimpleInsertSelect("addForumPermsToForumStaff",
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
)*/
/*a.SimpleInsertSelect("addForumPermsToForumMembers",
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
)*/
return nil
}
// nolint
func writeInsertLeftJoins(a qgen.Adapter) error {
return nil
}
func writeInsertInnerJoins(a qgen.Adapter) error {
return nil
}
func writeFile(name, content string) (err error) {
f, err := os.Create(name)
if err != nil {
return err
}
_, err = f.WriteString(content)
if err != nil {
return err
}
err = f.Sync()
if err != nil {
return err
}
return f.Close()
}
================================================
FILE: cmd/query_gen/run.bat
================================================
@echo off
echo Building the query generator
go build -ldflags="-s -w"
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo The query generator was successfully built
query_gen.exe
pause
================================================
FILE: cmd/query_gen/spitter.go
================================================
package main
import "strings"
import "github.com/Azareal/Gosora/query_gen"
type PrimaryKeySpitter struct {
keys map[string]string
}
func NewPrimaryKeySpitter() *PrimaryKeySpitter {
return &PrimaryKeySpitter{make(map[string]string)}
}
func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
if name == "CreateTableStart" {
var found string
var table = args[0].(*qgen.DBInstallTable)
for _, key := range table.Keys {
if key.Type == "primary" {
expl := strings.Split(key.Columns, ",")
if len(expl) > 1 {
continue
}
found = key.Columns
}
if found != "" {
table := table.Name
spit.keys[table] = found
}
}
}
return nil
}
func (spit *PrimaryKeySpitter) Write() error {
out := `// Generated by Gosora's Query Generator. DO NOT EDIT.
package main
var dbTablePrimaryKeys = map[string]string{
`
for table, key := range spit.keys {
out += "\t\"" + table + "\":\"" + key + "\",\n"
}
return writeFile("./gen_tables.go", out+"}\n")
}
================================================
FILE: cmd/query_gen/tables.go
================================================
package main
import qgen "github.com/Azareal/Gosora/query_gen"
var mysqlPre = "utf8mb4"
var mysqlCol = "utf8mb4_general_ci"
var tables []string
type tblColumn = qgen.DBTableColumn
type tC = tblColumn
type tblKey = qgen.DBTableKey
func createTables(a qgen.Adapter) error {
tables = nil
f := func(table, charset, collation string, cols []tC, keys []tblKey) error {
tables = append(tables, table)
return qgen.Install.CreateTable(table, charset, collation, cols, keys)
}
return createTables2(a, f)
}
func createTables2(a qgen.Adapter, f func(table, charset, collation string, columns []tC, keys []tblKey) error) (err error) {
createTable := func(table, charset, collation string, cols []tC, keys []tblKey) {
if err != nil {
return
}
err = f(table, charset, collation, cols, keys)
}
bcol := func(col string, val bool) qgen.DBTableColumn {
if val {
return tC{col, "boolean", 0, false, false, "1"}
}
return tC{col, "boolean", 0, false, false, "0"}
}
ccol := func(col string, size int, sdefault string) qgen.DBTableColumn {
return tC{col, "varchar", size, false, false, sdefault}
}
text := func(params ...string) qgen.DBTableColumn {
if len(params) == 0 {
return tC{"", "text", 0, false, false, ""}
}
col, sdefault := params[0], ""
if len(params) > 1 {
sdefault = params[1]
if sdefault == "" {
sdefault = "''"
}
}
return tC{col, "text", 0, false, false, sdefault}
}
createdAt := func(coll ...string) qgen.DBTableColumn {
var col string
if len(coll) > 0 {
col = coll[0]
}
if col == "" {
col = "createdAt"
}
return tC{col, "createdAt", 0, false, false, ""}
}
createTable("users", mysqlPre, mysqlCol,
[]tC{
{"uid", "int", 0, false, true, ""},
ccol("name", 100, ""),
ccol("password", 100, ""),
ccol("salt", 80, "''"),
{"group", "int", 0, false, false, ""}, // TODO: Make this a foreign key
bcol("active", false),
bcol("is_super_admin", false),
createdAt(),
{"lastActiveAt", "datetime", 0, false, false, ""},
ccol("session", 200, "''"),
//ccol("authToken", 200, "''"),
ccol("last_ip", 200, "''"),
{"profile_comments", "int", 0, false, false, "0"},
{"who_can_convo", "int", 0, false, false, "0"},
{"enable_embeds", "int", 0, false, false, "-1"},
ccol("email", 200, "''"),
ccol("avatar", 100, "''"),
text("message"),
// TODO: Drop these columns?
ccol("url_prefix", 20, "''"),
ccol("url_name", 100, "''"),
//text("pub_key"),
{"level", "smallint", 0, false, false, "0"},
{"score", "int", 0, false, false, "0"},
{"posts", "int", 0, false, false, "0"},
{"bigposts", "int", 0, false, false, "0"},
{"megaposts", "int", 0, false, false, "0"},
{"topics", "int", 0, false, false, "0"},
{"liked", "int", 0, false, false, "0"},
// These two are to bound liked queries with little bits of information we know about the user to reduce the server load
{"oldestItemLikedCreatedAt", "datetime", 0, false, false, ""}, // For internal use only, semantics may change
{"lastLiked", "datetime", 0, false, false, ""}, // For internal use only, semantics may change
//{"penalty_count","int",0,false,false,"0"},
{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
},
[]tK{
{"uid", "primary", "", false},
{"name", "unique", "", false},
},
)
createTable("users_groups", mysqlPre, mysqlCol,
[]tC{
{"gid", "int", 0, false, true, ""},
ccol("name", 100, ""),
text("permissions"),
text("plugin_perms"),
bcol("is_mod", false),
bcol("is_admin", false),
bcol("is_banned", false),
{"user_count", "int", 0, false, false, "0"}, // TODO: Implement this
ccol("tag", 50, "''"),
},
[]tK{
{"gid", "primary", "", false},
},
)
createTable("users_groups_promotions", mysqlPre, mysqlCol,
[]tC{
{"pid", "int", 0, false, true, ""},
{"from_gid", "int", 0, false, false, ""},
{"to_gid", "int", 0, false, false, ""},
bcol("two_way", false), // If a user no longer meets the requirements for this promotion then they will be demoted if this flag is set
// Requirements
{"level", "int", 0, false, false, ""},
{"posts", "int", 0, false, false, "0"},
{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted
{"registeredFor", "int", 0, false, false, "0"}, // minutes
},
[]tK{
{"pid", "primary", "", false},
},
)
/*
createTable("users_groups_promotions_scheduled","","",
[]tC{
{"prid","int",0,false,false,""},
{"uid","int",0,false,false,""},
{"runAt","datetime",0,false,false,""},
},
[]tK{
// TODO: Test to see that the compound primary key works
{"prid,uid", "primary", "", false},
},
)
*/
createTable("users_2fa_keys", mysqlPre, mysqlCol,
[]tC{
{"uid", "int", 0, false, false, ""},
ccol("secret", 100, ""),
ccol("scratch1", 50, ""),
ccol("scratch2", 50, ""),
ccol("scratch3", 50, ""),
ccol("scratch4", 50, ""),
ccol("scratch5", 50, ""),
ccol("scratch6", 50, ""),
ccol("scratch7", 50, ""),
ccol("scratch8", 50, ""),
{"createdAt", "createdAt", 0, false, false, ""},
},
[]tK{
{"uid", "primary", "", false},
},
)
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
// TODO: Perm overrides
// TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag
// TODO: Add a penalty type where a user is stopped from creating plugin_guilds social groups
// TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly.
/*createTable("users_penalties","","",
[]tC{
{"uid","int",0,false,false,""},
{"element_id","int",0,false,false,""},
ccol("element_type",50,""), //forum, profile?, and social_group. Leave blank for global.
text("overrides","{}"),
bcol("mod_queue",false),
bcol("shadow_ban",false),
bcol("no_avatar",false), // Coming Soon. Should this be a perm override instead?
// Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something?
//{"posts_per_hour","int",0,false,false,"0"},
//{"topics_per_hour","int",0,false,false,"0"},
//{"posts_count","int",0,false,false,"0"},
//{"topic_count","int",0,false,false,"0"},
//{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters.
{"issued_by","int",0,false,false,""},
createdAt("issued_at"),
{"expires_at","datetime",0,false,false,""},
}, nil,
)*/
createTable("users_groups_scheduler", "", "",
[]tC{
{"uid", "int", 0, false, false, ""},
{"set_group", "int", 0, false, false, ""},
{"issued_by", "int", 0, false, false, ""},
createdAt("issued_at"),
{"revert_at", "datetime", 0, false, false, ""},
{"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future
},
[]tK{
{"uid", "primary", "", false},
},
)
// TODO: Can we use a piece of software dedicated to persistent queues for this rather than relying on the database for it?
createTable("users_avatar_queue", "", "",
[]tC{
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
},
[]tK{
{"uid", "primary", "", false},
},
)
// TODO: Should we add a users prefix to this table to fit the "unofficial convention"?
// TODO: Add an autoincrement key?
createTable("emails", "", "",
[]tC{
ccol("email", 200, ""),
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
bcol("validated", false),
ccol("token", 200, "''"),
}, nil,
)
// TODO: Allow for patterns in domains, if the bots try to shake things up there?
/*
createTable("email_domain_blacklist", "", "",
[]tC{
ccol("domain", 200, ""),
bcol("gtld", false),
},
[]tK{
{"domain", "primary"},
},
)
*/
// TODO: Implement password resets
createTable("password_resets", "", "",
[]tC{
ccol("email", 200, ""),
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token
ccol("token", 200, ""),
createdAt(),
}, nil,
)
createTable("forums", mysqlPre, mysqlCol,
[]tC{
{"fid", "int", 0, false, true, ""},
ccol("name", 100, ""),
ccol("desc", 200, ""),
ccol("tmpl", 200, "''"),
bcol("active", true),
{"order", "int", 0, false, false, "0"},
{"topicCount", "int", 0, false, false, "0"},
ccol("preset", 100, "''"),
{"parentID", "int", 0, false, false, "0"},
ccol("parentType", 50, "''"),
{"lastTopicID", "int", 0, false, false, "0"},
{"lastReplyerID", "int", 0, false, false, "0"},
},
[]tK{
{"fid", "primary", "", false},
},
)
createTable("forums_permissions", "", "",
[]tC{
{"fid", "int", 0, false, false, ""},
{"gid", "int", 0, false, false, ""},
ccol("preset", 100, "''"),
text("permissions", "{}"),
},
[]tK{
// TODO: Test to see that the compound primary key works
{"fid,gid", "primary", "", false},
},
)
createTable("topics", mysqlPre, mysqlCol,
[]tC{
{"tid", "int", 0, false, true, ""},
ccol("title", 100, ""), // TODO: Increase the max length to 200?
text("content"),
text("parsed_content"),
createdAt(),
{"lastReplyAt", "datetime", 0, false, false, ""},
{"lastReplyBy", "int", 0, false, false, ""},
{"lastReplyID", "int", 0, false, false, "0"},
{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
bcol("is_closed", false),
bcol("sticky", false),
// TODO: Add an index for this
{"parentID", "int", 0, false, false, "2"},
ccol("ip", 200, "''"),
{"postCount", "int", 0, false, false, "1"},
{"likeCount", "int", 0, false, false, "0"},
{"attachCount", "int", 0, false, false, "0"},
{"words", "int", 0, false, false, "0"},
{"views", "int", 0, false, false, "0"},
//{"dayViews", "int", 0, false, false, "0"},
{"weekEvenViews", "int", 0, false, false, "0"},
{"weekOddViews", "int", 0, false, false, "0"},
///{"weekViews", "int", 0, false, false, "0"},
///{"lastWeekViews", "int", 0, false, false, "0"},
//{"monthViews", "int", 0, false, false, "0"},
// ? - A little hacky, maybe we could do something less likely to bite us with huge numbers of topics?
// TODO: Add an index for this?
//{"lastMonth", "datetime", 0, false, false, ""},
ccol("css_class", 100, "''"),
{"poll", "int", 0, false, false, "0"},
ccol("data", 200, "''"),
},
[]tK{
{"tid", "primary", "", false},
{"title", "fulltext", "", false},
{"content", "fulltext", "", false},
},
)
createTable("replies", mysqlPre, mysqlCol,
[]tC{
{"rid", "int", 0, false, true, ""}, // TODO: Rename to replyID?
{"tid", "int", 0, false, false, ""}, // TODO: Rename to topicID?
text("content"),
text("parsed_content"),
createdAt(),
{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"lastEdit", "int", 0, false, false, "0"},
{"lastEditBy", "int", 0, false, false, "0"},
{"lastUpdated", "datetime", 0, false, false, ""},
ccol("ip", 200, "''"),
{"likeCount", "int", 0, false, false, "0"},
{"attachCount", "int", 0, false, false, "0"},
{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
ccol("actionType", 20, "''"),
{"poll", "int", 0, false, false, "0"},
},
[]tK{
{"rid", "primary", "", false},
{"content", "fulltext", "", false},
},
)
createTable("attachments", mysqlPre, mysqlCol,
[]tC{
{"attachID", "int", 0, false, true, ""},
{"sectionID", "int", 0, false, false, "0"},
ccol("sectionTable", 200, "forums"),
{"originID", "int", 0, false, false, ""},
ccol("originTable", 200, "replies"),
{"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key
ccol("path", 200, ""),
ccol("extra", 200, ""),
},
[]tK{
{"attachID", "primary", "", false},
},
)
createTable("revisions", mysqlPre, mysqlCol,
[]tC{
{"reviseID", "int", 0, false, true, ""},
text("content"),
{"contentID", "int", 0, false, false, ""},
ccol("contentType", 100, "replies"),
createdAt(),
// TODO: Add a createdBy column?
},
[]tK{
{"reviseID", "primary", "", false},
},
)
createTable("polls", mysqlPre, mysqlCol,
[]tC{
{"pollID", "int", 0, false, true, ""},
{"parentID", "int", 0, false, false, "0"},
ccol("parentTable", 100, "topics"), // topics, replies
{"type", "int", 0, false, false, "0"},
{"options", "json", 0, false, false, ""},
{"votes", "int", 0, false, false, "0"},
},
[]tK{
{"pollID", "primary", "", false},
},
)
createTable("polls_options", "", "",
[]tC{
{"pollID", "int", 0, false, false, ""},
{"option", "int", 0, false, false, "0"},
{"votes", "int", 0, false, false, "0"},
}, nil,
)
createTable("polls_votes", mysqlPre, mysqlCol,
[]tC{
{"pollID", "int", 0, false, false, ""},
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"option", "int", 0, false, false, "0"},
createdAt("castAt"),
ccol("ip", 200, "''"),
}, nil,
)
createTable("users_replies", mysqlPre, mysqlCol,
[]tC{
{"rid", "int", 0, false, true, ""},
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
text("content"),
text("parsed_content"),
createdAt(),
{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"lastEdit", "int", 0, false, false, "0"},
{"lastEditBy", "int", 0, false, false, "0"},
ccol("ip", 200, "''"),
},
[]tK{
{"rid", "primary", "", false},
},
)
createTable("likes", "", "",
[]tC{
{"weight", "tinyint", 0, false, false, "1"},
{"targetItem", "int", 0, false, false, ""},
ccol("targetType", 50, "replies"),
{"sentBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
createdAt(),
{"recalc", "tinyint", 0, false, false, "0"},
}, nil,
)
//columns("participants,createdBy,createdAt,lastReplyBy,lastReplyAt").Where("cid=?")
createTable("conversations", "", "",
[]tC{
{"cid", "int", 0, false, true, ""},
{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
createdAt(),
{"lastReplyAt", "datetime", 0, false, false, ""},
{"lastReplyBy", "int", 0, false, false, ""},
},
[]tK{
{"cid", "primary", "", false},
},
)
createTable("conversations_posts", "", "",
[]tC{
{"pid", "int", 0, false, true, ""},
{"cid", "int", 0, false, false, ""},
{"createdBy", "int", 0, false, false, ""},
ccol("body", 50, ""),
ccol("post", 50, "''"),
},
[]tK{
{"pid", "primary", "", false},
},
)
createTable("conversations_participants", "", "",
[]tC{
{"uid", "int", 0, false, false, ""},
{"cid", "int", 0, false, false, ""},
}, nil,
)
/*
createTable("users_friends", "", "",
[]tC{
{"uid", "int", 0, false, false, ""},
{"uid2", "int", 0, false, false, ""},
}, nil,
)
createTable("users_friends_invites", "", "",
[]tC{
{"requester", "int", 0, false, false, ""},
{"target", "int", 0, false, false, ""},
}, nil,
)
*/
createTable("users_blocks", "", "",
[]tC{
{"blocker", "int", 0, false, false, ""},
{"blockedUser", "int", 0, false, false, ""},
}, nil,
)
createTable("activity_stream_matches", "", "",
[]tC{
{"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
},
[]tK{
{"asid,asid", "foreign", "activity_stream", true},
},
)
createTable("activity_stream", "", "",
[]tC{
{"asid", "int", 0, false, true, ""},
{"actor", "int", 0, false, false, ""}, /* the one doing the act */ // TODO: Make this a foreign key
{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */
ccol("event", 50, ""), /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */
ccol("elementType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
// replacement for elementType
tC{"elementTable", "int", 0, false, false, "0"},
{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
createdAt(),
ccol("extra", 200, "''"),
},
[]tK{
{"asid", "primary", "", false},
},
)
createTable("activity_subscriptions", "", "",
[]tC{
{"user", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
ccol("targetType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/
}, nil,
)
/* Due to MySQL's design, we have to drop the unique keys for table settings, plugins, and themes down from 200 to 180 or it will error */
createTable("settings", "", "",
[]tC{
ccol("name", 180, ""),
ccol("content", 250, ""),
ccol("type", 50, ""),
ccol("constraints", 200, "''"),
},
[]tK{
{"name", "unique", "", false},
},
)
createTable("word_filters", "", "",
[]tC{
{"wfid", "int", 0, false, true, ""},
ccol("find", 200, ""),
ccol("replacement", 200, ""),
},
[]tK{
{"wfid", "primary", "", false},
},
)
createTable("plugins", "", "",
[]tC{
ccol("uname", 180, ""),
bcol("active", false),
bcol("installed", false),
},
[]tK{
{"uname", "unique", "", false},
},
)
createTable("themes", "", "",
[]tC{
ccol("uname", 180, ""),
bcol("default", false),
//text("profileUserVars"),
},
[]tK{
{"uname", "unique", "", false},
},
)
createTable("widgets", "", "",
[]tC{
{"wid", "int", 0, false, true, ""},
{"position", "int", 0, false, false, ""},
ccol("side", 100, ""),
ccol("type", 100, ""),
bcol("active", false),
ccol("location", 100, ""),
text("data"),
},
[]tK{
{"wid", "primary", "", false},
},
)
createTable("menus", "", "",
[]tC{
{"mid", "int", 0, false, true, ""},
},
[]tK{
{"mid", "primary", "", false},
},
)
createTable("menu_items", "", "",
[]tC{
{"miid", "int", 0, false, true, ""},
{"mid", "int", 0, false, false, ""},
ccol("name", 200, "''"),
ccol("htmlID", 200, "''"),
ccol("cssClass", 200, "''"),
ccol("position", 100, ""),
ccol("path", 200, "''"),
ccol("aria", 200, "''"),
ccol("tooltip", 200, "''"),
ccol("tmplName", 200, "''"),
{"order", "int", 0, false, false, "0"},
bcol("guestOnly", false),
bcol("memberOnly", false),
bcol("staffOnly", false),
bcol("adminOnly", false),
},
[]tK{
{"miid", "primary", "", false},
},
)
createTable("pages", mysqlPre, mysqlCol,
[]tC{
{"pid", "int", 0, false, true, ""},
//ccol("path", 200, ""),
ccol("name", 200, ""),
ccol("title", 200, ""),
text("body"),
// TODO: Make this a table?
text("allowedGroups"),
{"menuID", "int", 0, false, false, "-1"}, // simple sidebar menu
},
[]tK{
{"pid", "primary", "", false},
},
)
createTable("registration_logs", "", "",
[]tC{
{"rlid", "int", 0, false, true, ""},
ccol("username", 100, ""),
{"email", "varchar", 100, false, false, ""},
ccol("failureReason", 100, ""),
bcol("success", false), // Did this attempt succeed?
ccol("ipaddress", 200, ""),
createdAt("doneAt"),
},
[]tK{
{"rlid", "primary", "", false},
},
)
createTable("login_logs", "", "",
[]tC{
{"lid", "int", 0, false, true, ""},
{"uid", "int", 0, false, false, ""},
bcol("success", false), // Did this attempt succeed?
ccol("ipaddress", 200, ""),
createdAt("doneAt"),
},
[]tK{
{"lid", "primary", "", false},
},
)
createTable("moderation_logs", "", "",
[]tC{
ccol("action", 100, ""),
{"elementID", "int", 0, false, false, ""},
ccol("elementType", 100, ""),
ccol("ipaddress", 200, ""),
{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"doneAt", "datetime", 0, false, false, ""},
text("extra"),
}, nil,
)
createTable("administration_logs", "", "",
[]tC{
ccol("action", 100, ""),
{"elementID", "int", 0, false, false, ""},
ccol("elementType", 100, ""),
ccol("ipaddress", 200, ""),
{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"doneAt", "datetime", 0, false, false, ""},
text("extra"),
}, nil,
)
createTable("viewchunks", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"avg", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
ccol("route", 200, ""), // TODO: set a default empty here
}, nil,
)
createTable("viewchunks_agents", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
ccol("browser", 200, ""), // googlebot, firefox, opera, etc.
//ccol("version",0,""), // the version of the browser or bot
}, nil,
)
createTable("viewchunks_systems", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
ccol("system", 200, ""), // windows, android, unknown, etc.
}, nil,
)
createTable("viewchunks_langs", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
ccol("lang", 200, ""), // en, ru, etc.
}, nil,
)
createTable("viewchunks_referrers", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
ccol("domain", 200, ""),
}, nil,
)
createTable("viewchunks_forums", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
{"forum", "int", 0, false, false, ""},
}, nil,
)
createTable("topicchunks", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
// TODO: Add a column for the parent forum?
}, nil,
)
createTable("postchunks", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
// TODO: Add a column for the parent topic / profile?
}, nil,
)
createTable("memchunks", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"stack", "int", 0, false, false, "0"},
{"heap", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
}, nil,
)
createTable("perfchunks", "", "",
[]tC{
{"low", "int", 0, false, false, "0"},
{"high", "int", 0, false, false, "0"},
{"avg", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
}, nil,
)
createTable("sync", "", "",
[]tC{
{"last_update", "datetime", 0, false, false, ""},
}, nil,
)
createTable("updates", "", "",
[]tC{
{"dbVersion", "int", 0, false, false, "0"},
}, nil,
)
createTable("meta", "", "",
[]tC{
ccol("name", 200, ""),
ccol("value", 200, ""),
}, nil,
)
/*createTable("tables", "", "",
[]tC{
{"id", "int", 0, false, true, ""},
ccol("name", 200, ""),
},
[]tK{
{"id", "primary", "", false},
{"name", "unique", "", false},
},
)*/
return err
}
================================================
FILE: common/activity_stream.go
================================================
package common
import (
"database/sql"
qgen "github.com/Azareal/Gosora/query_gen"
)
var Activity ActivityStream
type ActivityStream interface {
Add(a Alert) (int, error)
Get(id int) (Alert, error)
Delete(id int) error
DeleteByParams(event string, targetID int, targetType string) error
DeleteByParamsExtra(event string, targetID int, targetType, extra string) error
AidsByParams(event string, elementID int, elementType string) (aids []int, err error)
AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, err error)
Count() (count int)
}
type DefaultActivityStream struct {
add *sql.Stmt
get *sql.Stmt
delete *sql.Stmt
deleteByParams *sql.Stmt
deleteByParamsExtra *sql.Stmt
aidsByParams *sql.Stmt
aidsByParamsExtra *sql.Stmt
count *sql.Stmt
}
func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, error) {
as := "activity_stream"
cols := "actor,targetUser,event,elementType,elementID,createdAt,extra"
return &DefaultActivityStream{
add: acc.Insert(as).Columns(cols).Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(),
get: acc.Select(as).Columns(cols).Where("asid=?").Prepare(),
delete: acc.Delete(as).Where("asid=?").Prepare(),
deleteByParams: acc.Delete(as).Where("event=? AND elementID=? AND elementType=?").Prepare(),
deleteByParamsExtra: acc.Delete(as).Where("event=? AND elementID=? AND elementType=? AND extra=?").Prepare(),
aidsByParams: acc.Select(as).Columns("asid").Where("event=? AND elementID=? AND elementType=?").Prepare(),
aidsByParamsExtra: acc.Select(as).Columns("asid").Where("event=? AND elementID=? AND elementType=? AND extra=?").Prepare(),
count: acc.Count(as).Prepare(),
}, acc.FirstError()
}
func (s *DefaultActivityStream) Add(a Alert) (int, error) {
res, err := s.add.Exec(a.ActorID, a.TargetUserID, a.Event, a.ElementType, a.ElementID, a.Extra)
if err != nil {
return 0, err
}
lastID, err := res.LastInsertId()
return int(lastID), err
}
func (s *DefaultActivityStream) Get(id int) (Alert, error) {
a := Alert{ASID: id}
err := s.get.QueryRow(id).Scan(&a.ActorID, &a.TargetUserID, &a.Event, &a.ElementType, &a.ElementID, &a.CreatedAt, &a.Extra)
return a, err
}
func (s *DefaultActivityStream) Delete(id int) error {
_, err := s.delete.Exec(id)
return err
}
func (s *DefaultActivityStream) DeleteByParams(event string, elementID int, elementType string) error {
_, err := s.deleteByParams.Exec(event, elementID, elementType)
return err
}
func (s *DefaultActivityStream) DeleteByParamsExtra(event string, elementID int, elementType, extra string) error {
_, err := s.deleteByParamsExtra.Exec(event, elementID, elementType, extra)
return err
}
func (s *DefaultActivityStream) AidsByParams(event string, elementID int, elementType string) (aids []int, err error) {
rows, err := s.aidsByParams.Query(event, elementID, elementType)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var aid int
if err := rows.Scan(&aid); err != nil {
return nil, err
}
aids = append(aids, aid)
}
return aids, rows.Err()
}
func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, e error) {
rows, e := s.aidsByParamsExtra.Query(event, elementID, elementType, extra)
if e != nil {
return nil, e
}
defer rows.Close()
for rows.Next() {
var aid int
if e := rows.Scan(&aid); e != nil {
return nil, e
}
aids = append(aids, aid)
}
return aids, rows.Err()
}
// Count returns the total number of activity stream items
func (s *DefaultActivityStream) Count() (count int) {
return Count(s.count)
}
================================================
FILE: common/activity_stream_matches.go
================================================
package common
import (
"database/sql"
qgen "github.com/Azareal/Gosora/query_gen"
)
var ActivityMatches ActivityStreamMatches
type ActivityStreamMatches interface {
Add(watcher, asid int) error
Delete(watcher, asid int) error
DeleteAndCountChanged(watcher, asid int) (int, error)
CountAsid(asid int) int
}
type DefaultActivityStreamMatches struct {
add *sql.Stmt
delete *sql.Stmt
countAsid *sql.Stmt
}
func NewDefaultActivityStreamMatches(acc *qgen.Accumulator) (*DefaultActivityStreamMatches, error) {
asm := "activity_stream_matches"
return &DefaultActivityStreamMatches{
add: acc.Insert(asm).Columns("watcher,asid").Fields("?,?").Prepare(),
delete: acc.Delete(asm).Where("watcher=? AND asid=?").Prepare(),
countAsid: acc.Count(asm).Where("asid=?").Prepare(),
}, acc.FirstError()
}
func (s *DefaultActivityStreamMatches) Add(watcher, asid int) error {
_, e := s.add.Exec(watcher, asid)
return e
}
func (s *DefaultActivityStreamMatches) Delete(watcher, asid int) error {
_, e := s.delete.Exec(watcher, asid)
return e
}
func (s *DefaultActivityStreamMatches) DeleteAndCountChanged(watcher, asid int) (int, error) {
res, e := s.delete.Exec(watcher, asid)
if e != nil {
return 0, e
}
c64, e := res.RowsAffected()
return int(c64), e
}
func (s *DefaultActivityStreamMatches) CountAsid(asid int) int {
return Countf(s.countAsid, asid)
}
================================================
FILE: common/alerts/tmpls.go
================================================
package alerts
// TODO: Move the other alert related stuff to package alerts, maybe move notification logic here too?
type AlertItem struct {
ASID int
Path string
Message string
Avatar string
}
================================================
FILE: common/alerts.go
================================================
/*
*
* Gosora Alerts System
* Copyright Azareal 2017 - 2020
*
*/
package common
import (
"database/sql"
"errors"
"strconv"
"strings"
"time"
//"fmt"
"github.com/Azareal/Gosora/common/phrases"
qgen "github.com/Azareal/Gosora/query_gen"
)
type Alert struct {
ASID int
ActorID int
TargetUserID int
Event string
ElementType string
ElementID int
CreatedAt time.Time
Extra string
Actor *User
}
type AlertStmts struct {
notifyWatchers *sql.Stmt
getWatchers *sql.Stmt
}
var alertStmts AlertStmts
// TODO: Move these statements into some sort of activity abstraction
// TODO: Rewrite the alerts logic
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
alertStmts = AlertStmts{
notifyWatchers: acc.SimpleInsertInnerJoin(
qgen.DBInsert{"activity_stream_matches", "watcher,asid", ""},
qgen.DBJoin{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid=?", "", ""},
),
getWatchers: acc.SimpleInnerJoin("activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid=?", "", ""),
}
return acc.FirstError()
})
}
const AlertsGrowHint = len(`{"msgs":[],"count":,"tc":}`) + 1 + 10
// TODO: See if we can json.Marshal instead?
func escapeTextInJson(in string) string {
in = strings.Replace(in, "\"", "\\\"", -1)
return strings.Replace(in, "/", "\\/", -1)
}
func BuildAlert(a Alert, user User /* The current user */) (out string, err error) {
var targetUser *User
if a.Actor == nil {
a.Actor, err = Users.Get(a.ActorID)
if err != nil {
return "", errors.New(phrases.GetErrorPhrase("alerts_no_actor"))
}
}
/*if a.ElementType != "forum" {
targetUser, err = users.Get(a.TargetUserID)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
}*/
if a.Event == "friend_invite" {
return buildAlertString(".new_friend_invite", []string{a.Actor.Name}, a.Actor.Link, a.Actor.Avatar, a.ASID), nil
}
// Not that many events for us to handle in a forum
if a.ElementType == "forum" {
if a.Event == "reply" {
topic, err := Topics.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked topic %d", a.ElementID)
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
}
// Store the forum ID in the targetUser column instead of making a new one? o.O
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
return buildAlertString(".forum_new_topic", []string{a.Actor.Name, topic.Title}, topic.Link, a.Actor.Avatar, a.ASID), nil
}
return buildAlertString(".forum_unknown_action", []string{a.Actor.Name}, "", a.Actor.Avatar, a.ASID), nil
}
var url, area, phraseName string
own := false
// TODO: Avoid loading a bit of data twice
switch a.ElementType {
case "convo":
convo, err := Convos.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked convo %d", a.ElementID)
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_convo"))
}
url = convo.Link
case "topic":
topic, err := Topics.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked topic %d", a.ElementID)
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
}
url = topic.Link
area = topic.Title
own = a.TargetUserID == user.ID
case "user":
targetUser, err = Users.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find target user %d", a.ElementID)
return "", errors.New(phrases.GetErrorPhrase("alerts_no_target_user"))
}
area = targetUser.Name
url = targetUser.Link
own = a.TargetUserID == user.ID
case "post":
topic, err := TopicByReplyID(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked topic by reply ID %d", a.ElementID)
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic_by_reply"))
}
url = topic.Link
area = topic.Title
own = a.TargetUserID == user.ID
default:
return "", errors.New(phrases.GetErrorPhrase("alerts_invalid_elementtype"))
}
badEv := false
switch a.Event {
case "create", "like", "mention", "reply":
// skip
default:
badEv = true
}
if own && !badEv {
phraseName = "." + a.ElementType + "_own_" + a.Event
} else if !badEv {
phraseName = "." + a.ElementType + "_" + a.Event
} else if own {
phraseName = "." + a.ElementType + "_own"
} else {
phraseName = "." + a.ElementType
}
return buildAlertString(phraseName, []string{a.Actor.Name, area}, url, a.Actor.Avatar, a.ASID), nil
}
func buildAlertString(msg string, sub []string, path, avatar string, asid int) string {
var sb strings.Builder
buildAlertSb(&sb, msg, sub, path, avatar, asid)
return sb.String()
}
const AlertsGrowHint2 = len(`{"msg":"","sub":[],"path":"","img":"","id":}`) + 5 + 3 + 1 + 1 + 1
// TODO: Use a string builder?
func buildAlertSb(sb *strings.Builder, msg string, sub []string, path, avatar string, asid int) {
sb.WriteString(`{"msg":"`)
sb.WriteString(escapeTextInJson(msg))
sb.WriteString(`","sub":[`)
for i, it := range sub {
if i != 0 {
sb.WriteString(",\"")
} else {
sb.WriteString("\"")
}
sb.WriteString(escapeTextInJson(it))
sb.WriteString("\"")
}
sb.WriteString(`],"path":"`)
sb.WriteString(escapeTextInJson(path))
sb.WriteString(`","img":"`)
sb.WriteString(escapeTextInJson(avatar))
sb.WriteString(`","id":`)
sb.WriteString(strconv.Itoa(asid))
sb.WriteRune('}')
}
func BuildAlertSb(sb *strings.Builder, a *Alert, u *User /* The current user */) (err error) {
var targetUser *User
if a.Actor == nil {
a.Actor, err = Users.Get(a.ActorID)
if err != nil {
return errors.New(phrases.GetErrorPhrase("alerts_no_actor"))
}
}
/*if a.ElementType != "forum" {
targetUser, err = users.Get(a.TargetUserID)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
}*/
if a.Event == "friend_invite" {
buildAlertSb(sb, ".new_friend_invite", []string{a.Actor.Name}, a.Actor.Link, a.Actor.Avatar, a.ASID)
return nil
}
// Not that many events for us to handle in a forum
if a.ElementType == "forum" {
if a.Event == "reply" {
topic, err := Topics.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked topic %d", a.ElementID)
return errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
}
// Store the forum ID in the targetUser column instead of making a new one? o.O
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
buildAlertSb(sb, ".forum_new_topic", []string{a.Actor.Name, topic.Title}, topic.Link, a.Actor.Avatar, a.ASID)
return nil
}
buildAlertSb(sb, ".forum_unknown_action", []string{a.Actor.Name}, "", a.Actor.Avatar, a.ASID)
return nil
}
var url, area string
own := false
// TODO: Avoid loading a bit of data twice
switch a.ElementType {
case "convo":
convo, err := Convos.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked convo %d", a.ElementID)
return errors.New(phrases.GetErrorPhrase("alerts_no_linked_convo"))
}
url = convo.Link
case "topic":
topic, err := Topics.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked topic %d", a.ElementID)
return errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
}
url = topic.Link
area = topic.Title
own = a.TargetUserID == u.ID
case "user":
targetUser, err = Users.Get(a.ElementID)
if err != nil {
DebugLogf("Unable to find target user %d", a.ElementID)
return errors.New(phrases.GetErrorPhrase("alerts_no_target_user"))
}
area = targetUser.Name
url = targetUser.Link
own = a.TargetUserID == u.ID
case "post":
t, err := TopicByReplyID(a.ElementID)
if err != nil {
DebugLogf("Unable to find linked topic by reply ID %d", a.ElementID)
return errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic_by_reply"))
}
url = t.Link
area = t.Title
own = a.TargetUserID == u.ID
default:
return errors.New(phrases.GetErrorPhrase("alerts_invalid_elementtype"))
}
sb.WriteString(`{"msg":".`)
sb.WriteString(a.ElementType)
if own {
sb.WriteString("_own_")
} else {
sb.WriteRune('_')
}
switch a.Event {
case "create", "like", "mention", "reply":
sb.WriteString(a.Event)
}
sb.WriteString(`","sub":["`)
sb.WriteString(escapeTextInJson(a.Actor.Name))
sb.WriteString("\",\"")
sb.WriteString(escapeTextInJson(area))
sb.WriteString(`"],"path":"`)
sb.WriteString(escapeTextInJson(url))
sb.WriteString(`","img":"`)
sb.WriteString(escapeTextInJson(a.Actor.Avatar))
sb.WriteString(`","id":`)
sb.WriteString(strconv.Itoa(a.ASID))
sb.WriteRune('}')
return nil
}
//const AlertsGrowHint3 = len(`{"msg":"._","sub":["",""],"path":"","img":"","id":}`) + 3 + 2 + 2 + 2 + 2 + 1
// TODO: Create a notifier structure?
func AddActivityAndNotifyAll(a Alert) error {
id, err := Activity.Add(a)
if err != nil {
return err
}
return NotifyWatchers(id)
}
// TODO: Create a notifier structure?
func AddActivityAndNotifyTarget(a Alert) error {
id, err := Activity.Add(a)
if err != nil {
return err
}
err = ActivityMatches.Add(a.TargetUserID, id)
if err != nil {
return err
}
a.ASID = id
// Live alerts, if the target is online and WebSockets is enabled
if EnableWebsockets {
go func() {
defer EatPanics()
_ = WsHub.pushAlert(a.TargetUserID, a)
//fmt.Println("err:",err)
}()
}
return nil
}
// TODO: Create a notifier structure?
func NotifyWatchers(asid int) error {
_, err := alertStmts.notifyWatchers.Exec(asid)
if err != nil {
return err
}
// Alert the subscribers about this without blocking us from doing something else
if EnableWebsockets {
go func() {
defer EatPanics()
notifyWatchers(asid)
}()
}
return nil
}
func notifyWatchers(asid int) {
rows, e := alertStmts.getWatchers.Query(asid)
if e != nil && e != ErrNoRows {
LogError(e)
return
}
defer rows.Close()
var uid int
var uids []int
for rows.Next() {
if e := rows.Scan(&uid); e != nil {
LogError(e)
return
}
uids = append(uids, uid)
}
if e = rows.Err(); e != nil {
LogError(e)
return
}
alert, e := Activity.Get(asid)
if e != nil && e != ErrNoRows {
LogError(e)
return
}
_ = WsHub.pushAlerts(uids, alert)
}
func DismissAlert(uid, aid int) {
_ = WsHub.PushMessage(uid, `{"event":"dismiss-alert","id":`+strconv.Itoa(aid)+`}`)
}
================================================
FILE: common/analytics.go
================================================
package common
import (
"database/sql"
"log"
"time"
qgen "github.com/Azareal/Gosora/query_gen"
)
var Analytics AnalyticsStore
type AnalyticsTimeRange struct {
Quantity int
Unit string
Slices int
SliceWidth int
Range string
}
type AnalyticsStore interface {
FillViewMap(tbl string, tr *AnalyticsTimeRange, labelList []int64, viewMap map[int64]int64, param string, args ...interface{}) (map[int64]int64, error)
}
type DefaultAnalytics struct {
}
func NewDefaultAnalytics() *DefaultAnalytics {
return &DefaultAnalytics{}
}
/*
rows, e := qgen.NewAcc().Select("viewchunks_systems").Columns("count,createdAt").Where("system=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system)
if e != nil && e != sql.ErrNoRows {
return c.InternalError(e, w, r)
}
viewMap, e = c.AnalyticsRowsToViewMap(rows, labelList, viewMap)
if e != nil {
return c.InternalError(e, w, r)
}
*/
func (s *DefaultAnalytics) FillViewMap(tbl string, tr *AnalyticsTimeRange, labelList []int64, viewMap map[int64]int64, param string, args ...interface{}) (map[int64]int64, error) {
ac := qgen.NewAcc().Select(tbl).Columns("count,createdAt")
if param != "" {
ac = ac.Where(param + "=?")
}
rows, e := ac.DateCutoff("createdAt", tr.Quantity, tr.Unit).Query(args...)
if e != nil && e != sql.ErrNoRows {
return nil, e
}
return AnalyticsRowsToViewMap(rows, labelList, viewMap)
}
// TODO: Clamp it rather than using an offset off the current time to avoid chaotic changes in stats as adjacent sets converge and diverge?
func AnalyticsTimeRangeToLabelList(tr *AnalyticsTimeRange) (revLabelList []int64, labelList []int64, viewMap map[int64]int64) {
viewMap = make(map[int64]int64)
currentTime := time.Now().Unix()
for i := 1; i <= tr.Slices; i++ {
label := currentTime - int64(i*tr.SliceWidth)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
labelList = append(labelList, revLabelList...)
return revLabelList, labelList, viewMap
}
func AnalyticsRowsToViewMap(rows *sql.Rows, labelList []int64, viewMap map[int64]int64) (map[int64]int64, error) {
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
e := rows.Scan(&count, &createdAt)
if e != nil {
return viewMap, e
}
unixCreatedAt := createdAt.Unix()
// TODO: Bulk log this
if Dev.SuperDebug {
log.Print("count: ", count)
log.Print("createdAt: ", createdAt, " - ", unixCreatedAt)
}
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
return viewMap, rows.Err()
}
================================================
FILE: common/attachments.go
================================================
package common
import (
"database/sql"
"errors"
//"fmt"
"os"
"path/filepath"
"strings"
qgen "github.com/Azareal/Gosora/query_gen"
)
var Attachments AttachmentStore
var ErrCorruptAttachPath = errors.New("corrupt attachment path")
type MiniAttachment struct {
ID int
SectionID int
OriginID int
UploadedBy int
Path string
Extra string
Image bool
Ext string
}
type Attachment struct {
ID int
SectionTable string
SectionID int
OriginTable string
OriginID int
UploadedBy int
Path string
Extra string
Image bool
Ext string
}
type AttachmentStore interface {
GetForRenderRoute(filename string, sid int, sectionTable string) (*Attachment, error)
FGet(id int) (*Attachment, error)
Get(id int) (*MiniAttachment, error)
MiniGetList(originTable string, originID int) (alist []*MiniAttachment, err error)
BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error)
Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path, extra string) (int, error)
MoveTo(sectionID, originID int, originTable string) error
MoveToByExtra(sectionID int, originTable, extra string) error
Count() int
CountIn(originTable string, oid int) int
CountInPath(path string) int
Delete(id int) error
AddLinked(otable string, oid int) (err error)
RemoveLinked(otable string, oid int) (err error)
}
type DefaultAttachmentStore struct {
getForRenderRoute *sql.Stmt
fget *sql.Stmt
get *sql.Stmt
getByObj *sql.Stmt
add *sql.Stmt
count *sql.Stmt
countIn *sql.Stmt
countInPath *sql.Stmt
move *sql.Stmt
moveByExtra *sql.Stmt
delete *sql.Stmt
replyUpdateAttachs *sql.Stmt
topicUpdateAttachs *sql.Stmt
}
func NewDefaultAttachmentStore(acc *qgen.Accumulator) (*DefaultAttachmentStore, error) {
a := "attachments"
return &DefaultAttachmentStore{
getForRenderRoute: acc.Select(a).Columns("sectionTable, originID, originTable, uploadedBy, path").Where("path=? AND sectionID=? AND sectionTable=?").Prepare(),
fget: acc.Select(a).Columns("originTable, originID, sectionTable, sectionID, uploadedBy, path, extra").Where("attachID=?").Prepare(),
get: acc.Select(a).Columns("originID, sectionID, uploadedBy, path, extra").Where("attachID=?").Prepare(),
getByObj: acc.Select(a).Columns("attachID, sectionID, uploadedBy, path, extra").Where("originTable=? AND originID=?").Prepare(),
add: acc.Insert(a).Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path, extra").Fields("?,?,?,?,?,?,?").Prepare(),
count: acc.Count(a).Prepare(),
countIn: acc.Count(a).Where("originTable=? and originID=?").Prepare(),
countInPath: acc.Count(a).Where("path=?").Prepare(),
move: acc.Update(a).Set("sectionID=?").Where("originID=? AND originTable=?").Prepare(),
moveByExtra: acc.Update(a).Set("sectionID=?").Where("originTable=? AND extra=?").Prepare(),
delete: acc.Delete(a).Where("attachID=?").Prepare(),
// TODO: Less race-y attachment count updates
replyUpdateAttachs: acc.Update("replies").Set("attachCount=?").Where("rid=?").Prepare(),
topicUpdateAttachs: acc.Update("topics").Set("attachCount=?").Where("tid=?").Prepare(),
}, acc.FirstError()
}
// TODO: Revamp this to make it less of a copy-paste from the original code in the route
// ! Lacks some attachment initialisation code
func (s *DefaultAttachmentStore) GetForRenderRoute(filename string, sid int, sectionTable string) (*Attachment, error) {
a := &Attachment{SectionID: sid}
e := s.getForRenderRoute.QueryRow(filename, sid, sectionTable).Scan(&a.SectionTable, &a.OriginID, &a.OriginTable, &a.UploadedBy, &a.Path)
// TODO: Initialise attachment struct fields?
return a, e
}
func (s *DefaultAttachmentStore) MiniGetList(originTable string, originID int) (alist []*MiniAttachment, err error) {
rows, err := s.getByObj.Query(originTable, originID)
defer rows.Close()
for rows.Next() {
a := &MiniAttachment{OriginID: originID}
err := rows.Scan(&a.ID, &a.SectionID, &a.UploadedBy, &a.Path, &a.Extra)
if err != nil {
return nil, err
}
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
if len(a.Ext) == 0 {
return nil, ErrCorruptAttachPath
}
a.Image = ImageFileExts.Contains(a.Ext)
alist = append(alist, a)
}
if err = rows.Err(); err != nil {
return nil, err
}
if len(alist) == 0 {
err = sql.ErrNoRows
}
return alist, err
}
func (s *DefaultAttachmentStore) BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error) {
if len(ids) == 0 {
return nil, sql.ErrNoRows
}
if len(ids) == 1 {
res, err := s.MiniGetList(originTable, ids[0])
return map[int][]*MiniAttachment{ids[0]: res}, err
}
amap = make(map[int][]*MiniAttachment)
var buffer []*MiniAttachment
var currentID int
rows, err := qgen.NewAcc().Select("attachments").Columns("attachID,sectionID,originID,uploadedBy,path").Where("originTable=?").In("originID", ids).Orderby("originID ASC").Query(originTable)
defer rows.Close()
for rows.Next() {
a := &MiniAttachment{}
err := rows.Scan(&a.ID, &a.SectionID, &a.OriginID, &a.UploadedBy, &a.Path)
if err != nil {
return nil, err
}
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
if len(a.Ext) == 0 {
return nil, ErrCorruptAttachPath
}
a.Image = ImageFileExts.Contains(a.Ext)
if currentID == 0 {
currentID = a.OriginID
}
if a.OriginID != currentID {
if len(buffer) > 0 {
amap[currentID] = buffer
currentID = a.OriginID
buffer = nil
}
}
buffer = append(buffer, a)
}
if len(buffer) > 0 {
amap[currentID] = buffer
}
return amap, rows.Err()
}
func (s *DefaultAttachmentStore) FGet(id int) (*Attachment, error) {
a := &Attachment{ID: id}
e := s.fget.QueryRow(id).Scan(&a.OriginTable, &a.OriginID, &a.SectionTable, &a.SectionID, &a.UploadedBy, &a.Path, &a.Extra)
if e != nil {
return nil, e
}
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
if len(a.Ext) == 0 {
return nil, ErrCorruptAttachPath
}
a.Image = ImageFileExts.Contains(a.Ext)
return a, nil
}
func (s *DefaultAttachmentStore) Get(id int) (*MiniAttachment, error) {
a := &MiniAttachment{ID: id}
err := s.get.QueryRow(id).Scan(&a.OriginID, &a.SectionID, &a.UploadedBy, &a.Path, &a.Extra)
if err != nil {
return nil, err
}
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
if len(a.Ext) == 0 {
return nil, ErrCorruptAttachPath
}
a.Image = ImageFileExts.Contains(a.Ext)
return a, nil
}
func (s *DefaultAttachmentStore) Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path, extra string) (int, error) {
res, err := s.add.Exec(sectionID, sectionTable, originID, originTable, uploadedBy, path, extra)
if err != nil {
return 0, err
}
lid, err := res.LastInsertId()
return int(lid), err
}
func (s *DefaultAttachmentStore) MoveTo(sectionID, originID int, originTable string) error {
_, err := s.move.Exec(sectionID, originID, originTable)
return err
}
func (s *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTable, extra string) error {
_, err := s.moveByExtra.Exec(sectionID, originTable, extra)
return err
}
func (s *DefaultAttachmentStore) Count() (count int) {
e := s.count.QueryRow().Scan(&count)
if e != nil {
LogError(e)
}
return count
}
func (s *DefaultAttachmentStore) CountIn(originTable string, oid int) (count int) {
e := s.countIn.QueryRow(originTable, oid).Scan(&count)
if e != nil {
LogError(e)
}
return count
}
func (s *DefaultAttachmentStore) CountInPath(path string) (count int) {
e := s.countInPath.QueryRow(path).Scan(&count)
if e != nil {
LogError(e)
}
return count
}
func (s *DefaultAttachmentStore) Delete(id int) error {
_, e := s.delete.Exec(id)
return e
}
// TODO: Split this out of this store
func (s *DefaultAttachmentStore) AddLinked(otable string, oid int) (err error) {
switch otable {
case "topics":
_, err = s.topicUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
if err != nil {
return err
}
err = Topics.Reload(oid)
case "replies":
_, err = s.replyUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
if err != nil {
return err
}
err = Rstore.GetCache().Remove(oid)
}
if err == sql.ErrNoRows {
err = nil
}
return err
}
// TODO: Split this out of this store
func (s *DefaultAttachmentStore) RemoveLinked(otable string, oid int) (err error) {
switch otable {
case "topics":
_, err = s.topicUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
if err != nil {
return err
}
if tc := Topics.GetCache(); tc != nil {
tc.Remove(oid)
}
case "replies":
_, err = s.replyUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
if err != nil {
return err
}
err = Rstore.GetCache().Remove(oid)
}
return err
}
// TODO: Add a table for the files and lock the file row when performing tasks related to the file
func DeleteAttachment(aid int) error {
a, err := Attachments.FGet(aid)
if err != nil {
return err
}
err = deleteAttachment(a)
if err != nil {
return err
}
_ = Attachments.RemoveLinked(a.OriginTable, a.OriginID)
return nil
}
func deleteAttachment(a *Attachment) error {
err := Attachments.Delete(a.ID)
if err != nil {
return err
}
count := Attachments.CountInPath(a.Path)
if count == 0 {
err := os.Remove("./attachs/" + a.Path)
if err != nil {
return err
}
}
return nil
}
================================================
FILE: common/audit_logs.go
================================================
package common
import (
"database/sql"
"time"
qgen "github.com/Azareal/Gosora/query_gen"
)
var ModLogs LogStore
var AdminLogs LogStore
type LogItem struct {
Action string
ElementID int
ElementType string
IP string
ActorID int
DoneAt string
Extra string
}
type LogStore interface {
Create(action string, elementID int, elementType, ip string, actorID int) (err error)
CreateExtra(action string, elementID int, elementType, ip string, actorID int, extra string) (err error)
Count() int
GetOffset(offset, perPage int) (logs []LogItem, err error)
}
type SQLModLogStore struct {
create *sql.Stmt
count *sql.Stmt
getOffset *sql.Stmt
}
func NewModLogStore(acc *qgen.Accumulator) (*SQLModLogStore, error) {
ml := "moderation_logs"
// TODO: Shorten name of ipaddress column to ip
cols := "action, elementID, elementType, ipaddress, actorID, doneAt, extra"
return &SQLModLogStore{
create: acc.Insert(ml).Columns(cols).Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(),
count: acc.Count(ml).Prepare(),
getOffset: acc.Select(ml).Columns(cols).Orderby("doneAt DESC").Limit("?,?").Prepare(),
}, acc.FirstError()
}
// TODO: Make a store for this?
func (s *SQLModLogStore) Create(action string, elementID int, elementType, ip string, actorID int) (err error) {
return s.CreateExtra(action, elementID, elementType, ip, actorID, "")
}
func (s *SQLModLogStore) CreateExtra(action string, elementID int, elementType, ip string, actorID int, extra string) (err error) {
_, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra)
return err
}
func (s *SQLModLogStore) Count() (count int) {
err := s.count.QueryRow().Scan(&count)
if err != nil {
LogError(err)
}
return count
}
func buildLogList(rows *sql.Rows) (logs []LogItem, err error) {
for rows.Next() {
var l LogItem
var doneAt time.Time
err := rows.Scan(&l.Action, &l.ElementID, &l.ElementType, &l.IP, &l.ActorID, &doneAt, &l.Extra)
if err != nil {
return logs, err
}
l.DoneAt = doneAt.Format("2006-01-02 15:04:05")
logs = append(logs, l)
}
return logs, rows.Err()
}
func (s *SQLModLogStore) GetOffset(offset, perPage int) (logs []LogItem, err error) {
rows, err := s.getOffset.Query(offset, perPage)
if err != nil {
return logs, err
}
defer rows.Close()
return buildLogList(rows)
}
type SQLAdminLogStore struct {
create *sql.Stmt
count *sql.Stmt
getOffset *sql.Stmt
}
func NewAdminLogStore(acc *qgen.Accumulator) (*SQLAdminLogStore, error) {
al := "administration_logs"
cols := "action, elementID, elementType, ipaddress, actorID, doneAt, extra"
return &SQLAdminLogStore{
create: acc.Insert(al).Columns(cols).Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(),
count: acc.Count(al).Prepare(),
getOffset: acc.Select(al).Columns(cols).Orderby("doneAt DESC").Limit("?,?").Prepare(),
}, acc.FirstError()
}
// TODO: Make a store for this?
func (s *SQLAdminLogStore) Create(action string, elementID int, elementType, ip string, actorID int) (err error) {
return s.CreateExtra(action, elementID, elementType, ip, actorID, "")
}
func (s *SQLAdminLogStore) CreateExtra(action string, elementID int, elementType, ip string, actorID int, extra string) (err error) {
_, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra)
return err
}
func (s *SQLAdminLogStore) Count() (count int) {
err := s.count.QueryRow().Scan(&count)
if err != nil {
LogError(err)
}
return count
}
func (s *SQLAdminLogStore) GetOffset(offset, perPage int) (logs []LogItem, err error) {
rows, err := s.getOffset.Query(offset, perPage)
if err != nil {
return logs, err
}
defer rows.Close()
return buildLogList(rows)
}
================================================
FILE: common/auth.go
================================================
/*
*
* Gosora Authentication Interface
* Copyright Azareal 2017 - 2020
*
*/
package common
import (
"crypto/sha256"
"crypto/subtle"
"database/sql"
"encoding/hex"
"errors"
"net/http"
"strconv"
"strings"
"github.com/Azareal/Gosora/common/gauth"
qgen "github.com/Azareal/Gosora/query_gen"
//"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
)
// TODO: Write more authentication tests
var Auth AuthInt
const SaltLength int = 32
const SessionLength int = 80
// ErrMismatchedHashAndPassword is thrown whenever a hash doesn't match it's unhashed password
var ErrMismatchedHashAndPassword = bcrypt.ErrMismatchedHashAndPassword
// nolint
var ErrHashNotExist = errors.New("We don't recognise that hashing algorithm")
var ErrTooFewHashParams = errors.New("You haven't provided enough hash parameters")
// ErrPasswordTooLong is silly, but we don't want bcrypt to bork on us
var ErrPasswordTooLong = errors.New("The password you selected is too long")
var ErrWrongPassword = errors.New("That's not the correct password.")
var ErrBadMFAToken = errors.New("I'm not sure where you got that from, but that's not a valid 2FA token")
var ErrWrongMFAToken = errors.New("That 2FA token isn't correct")
var ErrNoMFAToken = errors.New("This user doesn't have 2FA setup")
var ErrSecretError = errors.New("There was a glitch in the system. Please contact your local administrator.")
var ErrNoUserByName = errors.New("We couldn't find an account with that username.")
var DefaultHashAlgo = "bcrypt" // Override this in the configuration file, not here
//func(realPassword string, password string, salt string) (err error)
var CheckPasswordFuncs = map[string]func(string, string, string) error{
"bcrypt": BcryptCheckPassword,
//"argon2": Argon2CheckPassword,
}
//func(password string) (hashedPassword string, salt string, err error)
var GeneratePasswordFuncs = map[string]func(string) (string, string, error){
"bcrypt": BcryptGeneratePassword,
//"argon2": Argon2GeneratePassword,
}
// TODO: Redirect 2b to bcrypt too?
var HashPrefixes = map[string]string{
"$2a$": "bcrypt",
//"argon2$": "argon2",
}
// AuthInt is the main authentication interface.
type AuthInt interface {
Authenticate(name, password string) (uid int, err error, requiresExtraAuth bool)
ValidateMFAToken(mfaToken string, uid int) error
Logout(w http.ResponseWriter, uid int)
ForceLogout(uid int) error
SetCookies(w http.ResponseWriter, uid int, session string)
SetProvisionalCookies(w http.ResponseWriter, uid int, session, signedSession string) // To avoid logging someone in until they've passed the MFA check
GetCookies(r *http.Request) (uid int, session string, err error)
SessionCheck(w http.ResponseWriter, r *http.Request) (u *User, halt bool)
CreateSession(uid int) (session string, err error)
CreateProvisionalSession(uid int) (provSession, signedSession string, err error) // To avoid logging someone in until they've passed the MFA check
}
// DefaultAuth is the default authenticator used by Gosora, may be swapped with an alternate authenticator in some situations. E.g. To support LDAP.
type DefaultAuth struct {
login *sql.Stmt
logout *sql.Stmt
updateSession *sql.Stmt
}
// NewDefaultAuth is a factory for spitting out DefaultAuths
func NewDefaultAuth() (*DefaultAuth, error) {
acc := qgen.NewAcc()
return &DefaultAuth{
login: acc.Select("users").Columns("uid, password, salt").Where("name = ?").Prepare(),
logout: acc.Update("users").Set("session = ''").Where("uid = ?").Prepare(),
updateSession: acc.Update("users").Set("session = ?").Where("uid = ?").Prepare(),
}, acc.FirstError()
}
// Authenticate checks if a specific username and password is valid and returns the UID for the corresponding user, if so. Otherwise, a user safe error.
// IF MFA is enabled, then pass it back a flag telling the caller that authentication isn't complete yet
// TODO: Find a better way of handling errors we don't want to reach the user
func (auth *DefaultAuth) Authenticate(name, password string) (uid int, err error, requiresExtraAuth bool) {
var realPassword, salt string
err = auth.login.QueryRow(name).Scan(&uid, &realPassword, &salt)
if err == ErrNoRows {
return 0, ErrNoUserByName, false
} else if err != nil {
LogError(err)
return 0, ErrSecretError, false
}
err = CheckPassword(realPassword, password, salt)
if err == ErrMismatchedHashAndPassword {
return 0, ErrWrongPassword, false
} else if err != nil {
LogError(err)
return 0, ErrSecretError, false
}
_, err = MFAstore.Get(uid)
if err != sql.ErrNoRows && err != nil {
LogError(err)
return 0, ErrSecretError, false
}
if err != ErrNoRows {
return uid, nil, true
}
return uid, nil, false
}
func (auth *DefaultAuth) ValidateMFAToken(mfaToken string, uid int) error {
mfaItem, err := MFAstore.Get(uid)
if err != sql.ErrNoRows && err != nil {
LogError(err)
return ErrSecretError
}
if err == ErrNoRows {
return ErrNoMFAToken
}
ok, err := VerifyGAuthToken(mfaItem.Secret, mfaToken)
if err != nil {
return ErrBadMFAToken
}
if ok {
return nil
}
for i, scratch := range mfaItem.Scratch {
if subtle.ConstantTimeCompare([]byte(scratch), []byte(mfaToken)) == 1 {
err = mfaItem.BurnScratch(i)
if err != nil {
LogError(err)
return ErrSecretError
}
return nil
}
}
return ErrWrongMFAToken
}
// ForceLogout logs the user out of every computer, not just the one they logged out of
func (auth *DefaultAuth) ForceLogout(uid int) error {
_, err := auth.logout.Exec(uid)
if err != nil {
LogError(err)
return ErrSecretError
}
// Flush the user out of the cache
if uc := Users.GetCache(); uc != nil {
uc.Remove(uid)
}
return nil
}
func setCookie(w http.ResponseWriter, cookie *http.Cookie, sameSite string) {
if v := cookie.String(); v != "" {
switch sameSite {
case "lax":
v = v + "; SameSite=lax"
case "strict":
v = v + "; SameSite"
}
w.Header().Add("Set-Cookie", v)
}
}
func deleteCookie(w http.ResponseWriter, cookie *http.Cookie) {
cookie.MaxAge = -1
http.SetCookie(w, cookie)
}
// Logout logs you out of the computer you requested the logout for, but not the other computers you're logged in with
func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
cookie := http.Cookie{Name: "uid", Value: "", Path: "/"}
deleteCookie(w, &cookie)
cookie = http.Cookie{Name: "session", Value: "", Path: "/"}
deleteCookie(w, &cookie)
}
// TODO: Set the cookie domain
// SetCookies sets the two cookies required for the current user to be recognised as a specific user in future requests
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: int(Year)}
setCookie(w, &cookie, "lax")
cookie = http.Cookie{Name: "session", Value: session, Path: "/", MaxAge: int(Year)}
setCookie(w, &cookie, "lax")
}
// TODO: Set the cookie domain
// SetProvisionalCookies sets the two cookies required for guests to be recognised as having passed the initial login but not having passed the additional checks (e.g. multi-factor authentication)
func (auth *DefaultAuth) SetProvisionalCookies(w http.ResponseWriter, uid int, provSession, signedSession string) {
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: int(Year)}
setCookie(w, &cookie, "lax")
cookie = http.Cookie{Name: "provSession", Value: provSession, Path: "/", MaxAge: int(Year)}
setCookie(w, &cookie, "lax")
cookie = http.Cookie{Name: "signedSession", Value: signedSession, Path: "/", MaxAge: int(Year)}
setCookie(w, &cookie, "lax")
}
// GetCookies fetches the current user's session cookies
func (auth *DefaultAuth) GetCookies(r *http.Request) (uid int, session string, err error) {
// Are there any session cookies..?
cookie, err := r.Cookie("uid")
if err != nil {
return 0, "", err
}
uid, err = strconv.Atoi(cookie.Value)
if err != nil {
return 0, "", err
}
cookie, err = r.Cookie("session")
if err != nil {
return 0, "", err
}
return uid, cookie.Value, err
}
// SessionCheck checks if a user has session cookies and whether they're valid
func (auth *DefaultAuth) SessionCheck(w http.ResponseWriter, r *http.Request) (user *User, halt bool) {
uid, session, err := auth.GetCookies(r)
if err != nil {
return &GuestUser, false
}
// Is this session valid..?
user, err = Users.Get(uid)
if err == ErrNoRows {
return &GuestUser, false
} else if err != nil {
InternalError(err, w, r)
return &GuestUser, true
}
// We need to do a constant time compare, otherwise someone might be able to deduce the session character by character based on how long it takes to do the comparison. Change this at your own peril.
if user.Session == "" || subtle.ConstantTimeCompare([]byte(session), []byte(user.Session)) != 1 {
return &GuestUser, false
}
return user, false
}
// CreateSession generates a new session to allow a remote client to stay logged in as a specific user
func (auth *DefaultAuth) CreateSession(uid int) (session string, err error) {
session, err = GenerateSafeString(SessionLength)
if err != nil {
return "", err
}
_, err = auth.updateSession.Exec(session, uid)
if err != nil {
return "", err
}
// Flush the user data from the cache
ucache := Users.GetCache()
if ucache != nil {
ucache.Remove(uid)
}
return session, nil
}
func (auth *DefaultAuth) CreateProvisionalSession(uid int) (provSession, signedSession string, err error) {
provSession, err = GenerateSafeString(SessionLength)
if err != nil {
return "", "", err
}
h := sha256.New()
h.Write([]byte(SessionSigningKeyBox.Load().(string)))
h.Write([]byte(provSession))
h.Write([]byte(strconv.Itoa(uid)))
return provSession, hex.EncodeToString(h.Sum(nil)), nil
}
func CheckPassword(realPassword, password, salt string) (err error) {
blasted := strings.Split(realPassword, "$")
prefix := blasted[0]
if len(blasted) > 1 {
prefix += "$" + blasted[1] + "$"
}
algo, ok := HashPrefixes[prefix]
if !ok {
return ErrHashNotExist
}
checker := CheckPasswordFuncs[algo]
return checker(realPassword, password, salt)
}
func GeneratePassword(password string) (hash, salt string, err error) {
gen, ok := GeneratePasswordFuncs[DefaultHashAlgo]
if !ok {
return "", "", ErrHashNotExist
}
return gen(password)
}
func BcryptCheckPassword(realPassword, password, salt string) (err error) {
return bcrypt.CompareHashAndPassword([]byte(realPassword), []byte(password+salt))
}
// Note: The salt is in the hash, therefore the salt parameter is blank
func BcryptGeneratePassword(password string) (hash, salt string, err error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", "", err
}
return string(hashedPassword), salt, nil
}
/*const (
argon2Time uint32 = 3
argon2Memory uint32 = 32 * 1024
argon2Threads uint8 = 4
argon2KeyLen uint32 = 32
)
func Argon2CheckPassword(realPassword, password, salt string) (err error) {
split := strings.Split(realPassword, "$")
// TODO: Better validation
if len(split) < 5 {
return ErrTooFewHashParams
}
realKey, _ := base64.StdEncoding.DecodeString(split[len(split)-1])
time, _ := strconv.Atoi(split[1])
memory, _ := strconv.Atoi(split[2])
threads, _ := strconv.Atoi(split[3])
keyLen, _ := strconv.Atoi(split[4])
key := argon2.Key([]byte(password), []byte(salt), uint32(time), uint32(memory), uint8(threads), uint32(keyLen))
if subtle.ConstantTimeCompare(realKey, key) != 1 {
return ErrMismatchedHashAndPassword
}
return nil
}
func Argon2GeneratePassword(password string) (hash, salt string, err error) {
sbytes := make([]byte, SaltLength)
_, err = rand.Read(sbytes)
if err != nil {
return "", "", err
}
key := argon2.Key([]byte(password), sbytes, argon2Time, argon2Memory, argon2Threads, argon2KeyLen)
hash = base64.StdEncoding.EncodeToString(key)
return fmt.Sprintf("argon2$%d%d%d%d%s%s", argon2Time, argon2Memory, argon2Threads, argon2KeyLen, salt, hash), string(sbytes), nil
}
*/
// TODO: Test this with Google Authenticator proper
func FriendlyGAuthSecret(secret string) (out string) {
for i, char := range secret {
out += string(char)
if (i+1)%4 == 0 {
out += " "
}
}
return strings.TrimSpace(out)
}
func GenerateGAuthSecret() (string, error) {
return GenerateStd32SafeString(14)
}
func VerifyGAuthToken(secret, token string) (bool, error) {
trueToken, err := gauth.GetTOTPToken(secret)
return subtle.ConstantTimeCompare([]byte(trueToken), []byte(token)) == 1, err
}
================================================
FILE: common/cache.go
================================================
package common
import "errors"
// nolint
// ErrCacheDesync is thrown whenever a piece of data, for instance, a user is out of sync with the database. Currently unused.
var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TODO: A cross-server synchronisation mechanism
// ErrStoreCapacityOverflow is thrown whenever a datastore reaches it's maximum hard capacity. I'm not sure if this error is actually used. It might be, we should check
var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.") // nolint
// nolint
type DataStore interface {
DirtyGet(id int) interface{}
Get(id int) (interface{}, error)
BypassGet(id int) (interface{}, error)
//Count() int
}
// nolint
type DataCache interface {
CacheGet(id int) (interface{}, error)
CacheGetUnsafe(id int) (interface{}, error)
CacheSet(item interface{}) error
CacheAdd(item interface{}) error
CacheAddUnsafe(item interface{}) error
CacheRemove(id int) error
CacheRemoveUnsafe(id int) error
Reload(id int) error
Flush()
Length() int
SetCapacity(capacity int)
GetCapacity() int
}
================================================
FILE: common/common.go
================================================
/*
*
* Gosora Common Resources
* Copyright Azareal 2018 - 2020
*
*/
package common // import "github.com/Azareal/Gosora/common"
import (
"database/sql"
"io"
"log"
"net"
"net/http"
"os"
"runtime/debug"
"strconv"
"strings"
"sync/atomic"
"time"
meta "github.com/Azareal/Gosora/common/meta"
qgen "github.com/Azareal/Gosora/query_gen"
)
var SoftwareVersion = Version{Major: 0, Minor: 3, Patch: 0, Tag: "dev"}
var Meta meta.MetaStore
// nolint I don't want to write comments for each of these o.o
const Hour int = 60 * 60
const Day = Hour * 24
const Week = Day * 7
const Month = Day * 30
const Year = Day * 365
const Kilobyte int = 1024
const Megabyte = Kilobyte * 1024
const Gigabyte = Megabyte * 1024
const Terabyte = Gigabyte * 1024
const Petabyte = Terabyte * 1024
var StartTime time.Time
var GzipStartEtag string
var StartEtag string
var TmplPtrMap = make(map[string]interface{})
// Anti-spam token with rotated key
var JSTokenBox atomic.Value // TODO: Move this and some of these other globals somewhere else
var SessionSigningKeyBox atomic.Value // For MFA to avoid hitting the database unneccessarily
var OldSessionSigningKeyBox atomic.Value // Just in case we've signed with a key that's about to go stale so we don't annoy the user too much
var IsDBDown int32 = 0 // 0 = false, 1 = true. this is value which should be manipulated with package atomic for representing whether the database is down so we don't spam the log with lots of redundant errors
// ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores
var ErrNoRows = sql.ErrNoRows
//var StrSlicePool sync.Pool
// ? - Make this more customisable?
/*var ExternalSites = map[string]string{
"YT": "https://www.youtube.com/",
}*/
// TODO: Make this more customisable
var SpammyDomainBits = []string{"porn", "sex", "acup", "nude", "milf", "tits", "vape", "busty", "kink", "lingerie", "strapon", "problog", "fet", "xblog", "blogin", "blognetwork", "relayblog"}
var Chrome, Firefox int // ! Temporary Hack for http push
var SimpleBots []int // ! Temporary hack to stop semrush, ahrefs, python bots and other from wasting resources
type StringList []string
// ? - Should we allow users to upload .php or .go files? It could cause security issues. We could store them with a mangled extension to render them inert
// TODO: Let admins manage this from the Control Panel
// apng is commented out for now, as we have no way of re-encoding it into a smaller file
var AllowedFileExts = StringList{
"png", "jpg", "jpe", "jpeg", "jif", "jfi", "jfif", "svg", "bmp", "gif", "tiff", "tif", "webp", "apng", "avif", "flif", "heif", "heic", "bpg", // images (encodable) + apng (browser support) + bpg + avif + flif + heif / heic
"txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "eqcss", "pcss", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", /*"go","php",*/ // text
"wav", "mp3", "oga", "m4a", "flac", "ac3", "aac", "opus", // audio
"mp4", "avi", "ogg", "ogv", "ogx", "wmv", "webm", "flv", "f4v", "xvid", "mov", "movie", "qt", // video
"otf", "woff2", "woff", "ttf", "eot", // fonts
"bz2", "zip", "zipx", "gz", "7z", "tar", "cab", "rar", "kgb", "pea", "xz", "zz", "tgz", "xpi", // archives
"docx", "pdf", // documents
}
var ImageFileExts = StringList{
"png", "jpg", "jpe", "jpeg", "jif", "jfi", "jfif", "svg", "bmp", "gif", "tiff", "tif", "webp", /* "apng", "bpg", "avif", */
}
var TextFileExts = StringList{
"txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "eqcss", "pcss", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", /*"go","php",*/
}
var VideoFileExts = StringList{
"mp4", "avi", "ogg", "ogv", "ogx", "wmv", "webm", "flv", "f4v", "xvid", "mov", "movie", "qt",
}
var WebVideoFileExts = StringList{
"mp4", "avi", "ogg", "ogv", "webm",
}
var WebAudioFileExts = StringList{
"wav", "mp3", "oga", "m4a", "flac",
}
var ArchiveFileExts = StringList{
"bz2", "zip", "zipx", "gz", "7z", "tar", "cab", "rar", "kgb", "pea", "xz", "zz", "tgz", "xpi",
}
var ExecutableFileExts = StringList{
"exe", "jar", "phar", "shar", "iso", "apk", "deb",
}
func init() {
JSTokenBox.Store("")
SessionSigningKeyBox.Store("")
OldSessionSigningKeyBox.Store("")
}
// TODO: Write a test for this
func (sl StringList) Contains(needle string) bool {
for _, it := range sl {
if it == needle {
return true
}
}
return false
}
/*var DbTables []string
var TableToID = make(map[string]int)
var IDToTable = make(map[int]string)
func InitTables(acc *qgen.Accumulator) error {
stmt := acc.Select("tables").Columns("id,name").Prepare()
if e := acc.FirstError(); e != nil {
return e
}
return eachall(stmt, func(r *sql.Rows) error {
var id int
var name string
if e := r.Scan(&id, &name); e != nil {
return e
}
TableToID[name] = id
IDToTable[id] = name
return nil
})
}*/
type dbInits []func(acc *qgen.Accumulator) error
var DbInits dbInits
func (inits dbInits) Run() error {
for _, i := range inits {
if e := i(qgen.NewAcc()); e != nil {
return e
}
}
return nil
}
func (inits dbInits) Add(i ...func(acc *qgen.Accumulator) error) {
DbInits = dbInits(append(DbInits, i...))
}
// TODO: Add a graceful shutdown function
func StoppedServer(msg ...interface{}) {
//log.Print("stopped server")
StopServerChan <- msg
}
var StopServerChan = make(chan []interface{})
var LogWriter = io.MultiWriter(os.Stdout)
var ErrLogWriter = io.MultiWriter(os.Stderr)
var ErrLogger = log.New(os.Stderr, "", log.LstdFlags)
func DebugDetail(args ...interface{}) {
if Dev.SuperDebug {
log.Print(args...)
}
}
func DebugDetailf(str string, args ...interface{}) {
if Dev.SuperDebug {
log.Printf(str, args...)
}
}
func DebugLog(args ...interface{}) {
if Dev.DebugMode {
log.Print(args...)
}
}
func DebugLogf(str string, args ...interface{}) {
if Dev.DebugMode {
log.Printf(str, args...)
}
}
func Log(args ...interface{}) {
log.Print(args...)
}
func Logf(str string, args ...interface{}) {
log.Printf(str, args...)
}
func Err(args ...interface{}) {
ErrLogger.Print(args...)
}
func Count(stmt *sql.Stmt) (count int) {
e := stmt.QueryRow().Scan(&count)
if e != nil {
LogError(e)
}
return count
}
func Countf(stmt *sql.Stmt, args ...interface{}) (count int) {
e := stmt.QueryRow(args...).Scan(&count)
if e != nil {
LogError(e)
}
return count
}
func Createf(stmt *sql.Stmt, args ...interface{}) (id int, e error) {
res, e := stmt.Exec(args...)
if e != nil {
return 0, e
}
id64, e := res.LastInsertId()
return int(id64), e
}
func eachall(stmt *sql.Stmt, f func(r *sql.Rows) error) error {
rows, e := stmt.Query()
if e != nil {
return e
}
defer rows.Close()
for rows.Next() {
if e := f(rows); e != nil {
return e
}
}
return rows.Err()
}
var qcache = []string{0: "?", 1: "?,?", 2: "?,?,?", 3: "?,?,?,?", 4: "?,?,?,?,?", 5: "?,?,?,?,?,?", 6: "?,?,?,?,?,?,?", 7: "?,?,?,?,?,?,?,?", 8: "?,?,?,?,?,?,?,?,?"}
func inqbuild(ids []int) ([]interface{}, string) {
if len(ids) < 8 {
idList := make([]interface{}, len(ids))
for i, id := range ids {
idList[i] = strconv.Itoa(id)
}
return idList, qcache[len(ids)-1]
}
var sb strings.Builder
sb.Grow((len(ids) * 2) - 1)
idList := make([]interface{}, len(ids))
for i, id := range ids {
idList[i] = strconv.Itoa(id)
if i == 0 {
sb.WriteRune('?')
} else {
sb.WriteString(",?")
}
}
return idList, sb.String()
}
func inqbuild2(count int) string {
if count <= 8 {
return qcache[count-1]
}
var sb strings.Builder
sb.Grow((count * 2) - 1)
for i := 0; i < count; i++ {
if i == 0 {
sb.WriteRune('?')
} else {
sb.WriteString(",?")
}
}
return sb.String()
}
func inqbuildstr(strs []string) ([]interface{}, string) {
if len(strs) < 8 {
idList := make([]interface{}, len(strs))
for i, id := range strs {
idList[i] = id
}
return idList, qcache[len(strs)-1]
}
var sb strings.Builder
sb.Grow((len(strs) * 2) - 1)
idList := make([]interface{}, len(strs))
for i, id := range strs {
idList[i] = id
if i == 0 {
sb.WriteRune('?')
} else {
sb.WriteString(",?")
}
}
return idList, sb.String()
}
var ConnWatch = &ConnWatcher{}
type ConnWatcher struct {
n int64
}
func (cw *ConnWatcher) StateChange(conn net.Conn, state http.ConnState) {
switch state {
case http.StateNew:
atomic.AddInt64(&cw.n, 1)
case http.StateHijacked, http.StateClosed:
atomic.AddInt64(&cw.n, -1)
}
}
func (cw *ConnWatcher) Count() int {
return int(atomic.LoadInt64(&cw.n))
}
func EatPanics() {
if r := recover(); r != nil {
log.Print(r)
debug.PrintStack()
log.Fatal("Fatal error.")
}
}
================================================
FILE: common/common_easyjson.tgo
================================================
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package common
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon(in *jlexer.Lexer, out *WsTopicList) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "Topics":
if in.IsNull() {
in.Skip()
out.Topics = nil
} else {
in.Delim('[')
if out.Topics == nil {
if !in.IsDelim(']') {
out.Topics = make([]*WsTopicsRow, 0, 8)
} else {
out.Topics = []*WsTopicsRow{}
}
} else {
out.Topics = (out.Topics)[:0]
}
for !in.IsDelim(']') {
var v1 *WsTopicsRow
if in.IsNull() {
in.Skip()
v1 = nil
} else {
if v1 == nil {
v1 = new(WsTopicsRow)
}
easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon1(in, v1)
}
out.Topics = append(out.Topics, v1)
in.WantComma()
}
in.Delim(']')
}
case "LastPage":
out.LastPage = int(in.Int())
case "LastUpdate":
out.LastUpdate = int64(in.Int64())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon(out *jwriter.Writer, in WsTopicList) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"Topics\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
if in.Topics == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v2, v3 := range in.Topics {
if v2 > 0 {
out.RawByte(',')
}
if v3 == nil {
out.RawString("null")
} else {
easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon1(out, *v3)
}
}
out.RawByte(']')
}
}
{
const prefix string = ",\"LastPage\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.LastPage))
}
{
const prefix string = ",\"LastUpdate\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int64(int64(in.LastUpdate))
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v WsTopicList) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v WsTopicList) MarshalEasyJSON(w *jwriter.Writer) {
easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *WsTopicList) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *WsTopicList) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon(l, v)
}
func easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon1(in *jlexer.Lexer, out *WsTopicsRow) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "ID":
out.ID = int(in.Int())
case "Link":
out.Link = string(in.String())
case "Title":
out.Title = string(in.String())
case "CreatedBy":
out.CreatedBy = int(in.Int())
case "IsClosed":
out.IsClosed = bool(in.Bool())
case "Sticky":
out.Sticky = bool(in.Bool())
case "CreatedAt":
if data := in.Raw(); in.Ok() {
in.AddError((out.CreatedAt).UnmarshalJSON(data))
}
case "LastReplyAt":
if data := in.Raw(); in.Ok() {
in.AddError((out.LastReplyAt).UnmarshalJSON(data))
}
case "RelativeLastReplyAt":
out.RelativeLastReplyAt = string(in.String())
case "LastReplyBy":
out.LastReplyBy = int(in.Int())
case "LastReplyID":
out.LastReplyID = int(in.Int())
case "ParentID":
out.ParentID = int(in.Int())
case "ViewCount":
out.ViewCount = int64(in.Int64())
case "PostCount":
out.PostCount = int(in.Int())
case "LikeCount":
out.LikeCount = int(in.Int())
case "AttachCount":
out.AttachCount = int(in.Int())
case "ClassName":
out.ClassName = string(in.String())
case "Creator":
if in.IsNull() {
in.Skip()
out.Creator = nil
} else {
if out.Creator == nil {
out.Creator = new(WsJSONUser)
}
easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon2(in, out.Creator)
}
case "LastUser":
if in.IsNull() {
in.Skip()
out.LastUser = nil
} else {
if out.LastUser == nil {
out.LastUser = new(WsJSONUser)
}
easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon2(in, out.LastUser)
}
case "ForumName":
out.ForumName = string(in.String())
case "ForumLink":
out.ForumLink = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon1(out *jwriter.Writer, in WsTopicsRow) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"ID\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.ID))
}
{
const prefix string = ",\"Link\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Link))
}
{
const prefix string = ",\"Title\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Title))
}
{
const prefix string = ",\"CreatedBy\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.CreatedBy))
}
{
const prefix string = ",\"IsClosed\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.IsClosed))
}
{
const prefix string = ",\"Sticky\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.Sticky))
}
{
const prefix string = ",\"CreatedAt\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Raw((in.CreatedAt).MarshalJSON())
}
{
const prefix string = ",\"LastReplyAt\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Raw((in.LastReplyAt).MarshalJSON())
}
{
const prefix string = ",\"RelativeLastReplyAt\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.RelativeLastReplyAt))
}
{
const prefix string = ",\"LastReplyBy\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.LastReplyBy))
}
{
const prefix string = ",\"LastReplyID\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.LastReplyID))
}
{
const prefix string = ",\"ParentID\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.ParentID))
}
{
const prefix string = ",\"ViewCount\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int64(int64(in.ViewCount))
}
{
const prefix string = ",\"PostCount\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.PostCount))
}
{
const prefix string = ",\"LikeCount\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.LikeCount))
}
{
const prefix string = ",\"AttachCount\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.AttachCount))
}
{
const prefix string = ",\"ClassName\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.ClassName))
}
{
const prefix string = ",\"Creator\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
if in.Creator == nil {
out.RawString("null")
} else {
easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon2(out, *in.Creator)
}
}
{
const prefix string = ",\"LastUser\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
if in.LastUser == nil {
out.RawString("null")
} else {
easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon2(out, *in.LastUser)
}
}
{
const prefix string = ",\"ForumName\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.ForumName))
}
{
const prefix string = ",\"ForumLink\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.ForumLink))
}
out.RawByte('}')
}
func easyjsonC803d3e7DecodeGithubComAzarealGosoraCommon2(in *jlexer.Lexer, out *WsJSONUser) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "ID":
out.ID = int(in.Int())
case "Link":
out.Link = string(in.String())
case "Name":
out.Name = string(in.String())
case "Group":
out.Group = int(in.Int())
case "IsMod":
out.IsMod = bool(in.Bool())
case "Avatar":
out.Avatar = string(in.String())
case "MicroAvatar":
out.MicroAvatar = string(in.String())
case "Level":
out.Level = int(in.Int())
case "Score":
out.Score = int(in.Int())
case "Liked":
out.Liked = int(in.Int())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjsonC803d3e7EncodeGithubComAzarealGosoraCommon2(out *jwriter.Writer, in WsJSONUser) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"ID\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.ID))
}
{
const prefix string = ",\"Link\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Link))
}
{
const prefix string = ",\"Name\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Name))
}
{
const prefix string = ",\"Group\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.Group))
}
{
const prefix string = ",\"IsMod\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.IsMod))
}
{
const prefix string = ",\"Avatar\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Avatar))
}
{
const prefix string = ",\"MicroAvatar\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.MicroAvatar))
}
{
const prefix string = ",\"Level\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.Level))
}
{
const prefix string = ",\"Score\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.Score))
}
{
const prefix string = ",\"Liked\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.Liked))
}
out.RawByte('}')
}
================================================
FILE: common/conversations.go
================================================
package common
import (
"errors"
"time"
//"log"
"database/sql"
"strconv"
qgen "github.com/Azareal/Gosora/query_gen"
)
var Convos ConversationStore
var convoStmts ConvoStmts
type ConvoStmts struct {
fetchPost *sql.Stmt
getPosts *sql.Stmt
countPosts *sql.Stmt
edit *sql.Stmt
create *sql.Stmt
delete *sql.Stmt
has *sql.Stmt
editPost *sql.Stmt
createPost *sql.Stmt
deletePost *sql.Stmt
getUsers *sql.Stmt
}
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
cpo := "conversations_posts"
convoStmts = ConvoStmts{
fetchPost: acc.Select(cpo).Columns("cid,body,post,createdBy").Where("pid=?").Prepare(),
getPosts: acc.Select(cpo).Columns("pid,body,post,createdBy").Where("cid=?").Limit("?,?").Prepare(),
countPosts: acc.Count(cpo).Where("cid=?").Prepare(),
edit: acc.Update("conversations").Set("lastReplyBy=?,lastReplyAt=?").Where("cid=?").Prepare(),
create: acc.Insert("conversations").Columns("createdAt,lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
has: acc.Count("conversations_participants").Where("uid=? AND cid=?").Prepare(),
editPost: acc.Update(cpo).Set("body=?,post=?").Where("pid=?").Prepare(),
createPost: acc.Insert(cpo).Columns("cid,body,post,createdBy").Fields("?,?,?,?").Prepare(),
deletePost: acc.Delete(cpo).Where("pid=?").Prepare(),
getUsers: acc.Select("conversations_participants").Columns("uid").Where("cid=?").Prepare(),
}
return acc.FirstError()
})
}
type Conversation struct {
ID int
Link string
CreatedBy int
CreatedAt time.Time
LastReplyBy int
LastReplyAt time.Time
}
func (co *Conversation) Posts(offset, itemsPerPage int) (posts []*ConversationPost, err error) {
rows, err := convoStmts.getPosts.Query(co.ID, offset, itemsPerPage)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
p := &ConversationPost{CID: co.ID}
err := rows.Scan(&p.ID, &p.Body, &p.Post, &p.CreatedBy)
if err != nil {
return nil, err
}
p, err = ConvoPostProcess.OnLoad(p)
if err != nil {
return nil, err
}
posts = append(posts, p)
}
return posts, rows.Err()
}
func (co *Conversation) PostsCount() (count int) {
return Countf(convoStmts.countPosts, co.ID)
}
func (co *Conversation) Uids() (ids []int, err error) {
rows, e := convoStmts.getUsers.Query(co.ID)
if e != nil {
return nil, e
}
defer rows.Close()
for rows.Next() {
var id int
if e := rows.Scan(&id); e != nil {
return nil, e
}
ids = append(ids, id)
}
return ids, rows.Err()
}
func (co *Conversation) Has(uid int) (in bool) {
return Countf(convoStmts.has, uid, co.ID) > 0
}
func (co *Conversation) Update() error {
_, err := convoStmts.edit.Exec(co.CreatedAt, co.LastReplyBy, co.LastReplyAt, co.ID)
return err
}
func (co *Conversation) Create() (int, error) {
res, err := convoStmts.create.Exec()
if err != nil {
return 0, err
}
lastID, err := res.LastInsertId()
return int(lastID), err
}
func BuildConvoURL(coid int) string {
return "/user/convo/" + strconv.Itoa(coid)
}
type ConversationExtra struct {
*Conversation
Users []*User
}
type ConversationStore interface {
Get(id int) (*Conversation, error)
GetUser(uid, offset int) (cos []*Conversation, err error)
GetUserExtra(uid, offset int) (cos []*ConversationExtra, err error)
GetUserCount(uid int) (count int)
Delete(id int) error
Count() (count int)
Create(content string, createdBy int, participants []int) (int, error)
}
type DefaultConversationStore struct {
get *sql.Stmt
getUser *sql.Stmt
getUserCount *sql.Stmt
delete *sql.Stmt
deletePosts *sql.Stmt
deleteParticipants *sql.Stmt
create *sql.Stmt
addParticipant *sql.Stmt
count *sql.Stmt
}
func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConversationStore, error) {
co := "conversations"
return &DefaultConversationStore{
get: acc.Select(co).Columns("createdBy,createdAt,lastReplyBy,lastReplyAt").Where("cid=?").Prepare(),
getUser: acc.SimpleInnerJoin("conversations_participants AS cp", "conversations AS c", "cp.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt", "cp.cid=c.cid", "cp.uid=?", "c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC", "?,?"),
getUserCount: acc.Count("conversations_participants").Where("uid=?").Prepare(),
delete: acc.Delete(co).Where("cid=?").Prepare(),
deletePosts: acc.Delete("conversations_posts").Where("cid=?").Prepare(),
deleteParticipants: acc.Delete("conversations_participants").Where("cid=?").Prepare(),
create: acc.Insert(co).Columns("createdBy,createdAt,lastReplyBy,lastReplyAt").Fields("?,UTC_TIMESTAMP(),?,UTC_TIMESTAMP()").Prepare(),
addParticipant: acc.Insert("conversations_participants").Columns("uid,cid").Fields("?,?").Prepare(),
count: acc.Count(co).Prepare(),
}, acc.FirstError()
}
func (s *DefaultConversationStore) Get(id int) (*Conversation, error) {
co := &Conversation{ID: id}
err := s.get.QueryRow(id).Scan(&co.CreatedBy, &co.CreatedAt, &co.LastReplyBy, &co.LastReplyAt)
co.Link = BuildConvoURL(co.ID)
return co, err
}
func (s *DefaultConversationStore) GetUser(uid, offset int) (cos []*Conversation, err error) {
rows, err := s.getUser.Query(uid, offset, Config.ItemsPerPage)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
co := &Conversation{}
err := rows.Scan(&co.ID, &co.CreatedBy, &co.CreatedAt, &co.LastReplyBy, &co.LastReplyAt)
if err != nil {
return nil, err
}
co.Link = BuildConvoURL(co.ID)
cos = append(cos, co)
}
err = rows.Err()
if err != nil {
return nil, err
}
if len(cos) == 0 {
err = sql.ErrNoRows
}
return cos, err
}
func (s *DefaultConversationStore) GetUserExtra(uid, offset int) (cos []*ConversationExtra, err error) {
raw, err := s.GetUser(uid, offset)
if err != nil {
return nil, err
}
//log.Printf("raw: %+v\n", raw)
if len(raw) == 1 {
//log.Print("r0b2")
uids, err := raw[0].Uids()
if err != nil {
return nil, err
}
//log.Println("r1b2")
umap, err := Users.BulkGetMap(uids)
if err != nil {
return nil, err
}
//log.Println("r2b2")
users := make([]*User, len(umap))
var i int
for _, user := range umap {
users[i] = user
i++
}
return []*ConversationExtra{{raw[0], users}}, nil
}
//log.Println("1")
cmap := make(map[int]*ConversationExtra, len(raw))
for _, co := range raw {
cmap[co.ID] = &ConversationExtra{co, nil}
}
// TODO: Use inqbuild for this or a similar function
var q string
idList := make([]interface{}, len(raw))
for i, co := range raw {
if i == 0 {
q = "?"
} else {
q += ",?"
}
idList[i] = strconv.Itoa(co.ID)
}
rows, err := qgen.NewAcc().Select("conversations_participants").Columns("uid,cid").Where("cid IN(" + q + ")").Query(idList...)
if err != nil {
return nil, err
}
defer rows.Close()
//log.Println("2")
idmap := make(map[int][]int) // cid: []uid
puidmap := make(map[int]struct{})
for rows.Next() {
var uid, cid int
err := rows.Scan(&uid, &cid)
if err != nil {
return nil, err
}
idmap[cid] = append(idmap[cid], uid)
puidmap[uid] = struct{}{}
}
if err = rows.Err(); err != nil {
return nil, err
}
//log.Println("3")
//log.Printf("idmap: %+v\n", idmap)
//log.Printf("puidmap: %+v\n",puidmap)
puids := make([]int, len(puidmap))
var i int
for puid, _ := range puidmap {
puids[i] = puid
i++
}
umap, err := Users.BulkGetMap(puids)
if err != nil {
return nil, err
}
//log.Println("4")
//log.Printf("umap: %+v\n", umap)
for cid, uids := range idmap {
co := cmap[cid]
for _, uid := range uids {
co.Users = append(co.Users, umap[uid])
}
//log.Printf("co.Conversation: %+v\n", co.Conversation)
//log.Printf("co.Users: %+v\n", co.Users)
cmap[cid] = co
}
//log.Printf("cmap: %+v\n", cmap)
for _, ra := range raw {
cos = append(cos, cmap[ra.ID])
}
//log.Printf("cos: %+v\n", cos)
return cos, rows.Err()
}
func (s *DefaultConversationStore) GetUserCount(uid int) (count int) {
err := s.getUserCount.QueryRow(uid).Scan(&count)
if err != nil {
LogError(err)
}
return count
}
// TODO: Use a foreign key or transaction
func (s *DefaultConversationStore) Delete(id int) error {
_, err := s.delete.Exec(id)
if err != nil {
return err
}
_, err = s.deletePosts.Exec(id)
if err != nil {
return err
}
_, err = s.deleteParticipants.Exec(id)
return err
}
func (s *DefaultConversationStore) Create(content string, createdBy int, participants []int) (int, error) {
if len(participants) == 0 {
return 0, errors.New("no participants set")
}
res, err := s.create.Exec(createdBy, createdBy)
if err != nil {
return 0, err
}
lastID, err := res.LastInsertId()
if err != nil {
return 0, err
}
post := &ConversationPost{CID: int(lastID), Body: content, CreatedBy: createdBy}
_, err = post.Create()
if err != nil {
return 0, err
}
for _, p := range participants {
if p == createdBy {
continue
}
_, err := s.addParticipant.Exec(p, lastID)
if err != nil {
return 0, err
}
}
_, err = s.addParticipant.Exec(createdBy, lastID)
if err != nil {
return 0, err
}
return int(lastID), err
}
// Count returns the total number of topics on these forums
func (s *DefaultConversationStore) Count() (count int) {
err := s.count.QueryRow().Scan(&count)
if err != nil {
LogError(err)
}
return count
}
================================================
FILE: common/convos_posts.go
================================================
package common
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"io"
)
var ConvoPostProcess ConvoPostProcessor = NewDefaultConvoPostProcessor()
type ConvoPostProc
gitextract_v2tdrp1t/
├── .codebeatignore
├── .codeclimate.yml
├── .eslintrc.json
├── .gitignore
├── .htaccess
├── .travis.yml
├── .vscode/
│ └── settings.json
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── TODO.md
├── attachs/
│ └── filler.txt
├── backups/
│ └── filler.txt
├── build-linux
├── build-linux-nowebsockets
├── build-nowebsockets.bat
├── build.bat
├── build_templates.bat
├── cmd/
│ ├── common_hook_gen/
│ │ └── hookgen.go
│ ├── elasticsearch/
│ │ └── setup.go
│ ├── hook_gen/
│ │ └── main.go
│ ├── hook_stub_gen/
│ │ └── main.go
│ ├── install/
│ │ └── install.go
│ └── query_gen/
│ ├── build.bat
│ ├── main.go
│ ├── run.bat
│ ├── spitter.go
│ └── tables.go
├── common/
│ ├── activity_stream.go
│ ├── activity_stream_matches.go
│ ├── alerts/
│ │ └── tmpls.go
│ ├── alerts.go
│ ├── analytics.go
│ ├── attachments.go
│ ├── audit_logs.go
│ ├── auth.go
│ ├── cache.go
│ ├── common.go
│ ├── common_easyjson.tgo
│ ├── conversations.go
│ ├── convos_posts.go
│ ├── counters/
│ │ ├── agents.go
│ │ ├── common.go
│ │ ├── forums.go
│ │ ├── langs.go
│ │ ├── memory.go
│ │ ├── performance.go
│ │ ├── posts.go
│ │ ├── referrers.go
│ │ ├── requests.go
│ │ ├── routes.go
│ │ ├── systems.go
│ │ ├── topics.go
│ │ └── topics_views.go
│ ├── disk.go
│ ├── email.go
│ ├── email_store.go
│ ├── errors.go
│ ├── extend.go
│ ├── files.go
│ ├── forum.go
│ ├── forum_actions.go
│ ├── forum_perms.go
│ ├── forum_perms_store.go
│ ├── forum_store.go
│ ├── gauth/
│ │ └── authenticator.go
│ ├── group.go
│ ├── group_store.go
│ ├── ip_search.go
│ ├── likes.go
│ ├── menu_item_store.go
│ ├── menu_store.go
│ ├── menus.go
│ ├── meta/
│ │ └── meta_store.go
│ ├── mfa_store.go
│ ├── misc_logs.go
│ ├── module_ottojs.go
│ ├── no_websockets.go
│ ├── null_reply_cache.go
│ ├── null_topic_cache.go
│ ├── null_user_cache.go
│ ├── page_store.go
│ ├── pages.go
│ ├── parser.go
│ ├── password_reset.go
│ ├── permissions.go
│ ├── phrases/
│ │ └── phrases.go
│ ├── pluginlangs.go
│ ├── poll.go
│ ├── poll_cache.go
│ ├── poll_store.go
│ ├── profile_reply.go
│ ├── profile_reply_store.go
│ ├── promotions.go
│ ├── ratelimit.go
│ ├── recalc.go
│ ├── relations.go
│ ├── reply.go
│ ├── reply_cache.go
│ ├── reply_store.go
│ ├── report_store.go
│ ├── routes_common.go
│ ├── search.go
│ ├── settings.go
│ ├── site.go
│ ├── statistics.go
│ ├── subscription.go
│ ├── tasks.go
│ ├── template_init.go
│ ├── templates/
│ │ ├── context.go
│ │ ├── minifiers.go
│ │ └── templates.go
│ ├── thaw.go
│ ├── theme.go
│ ├── theme_list.go
│ ├── thumbnailer.go
│ ├── tickloop.go
│ ├── topic.go
│ ├── topic_cache.go
│ ├── topic_list.go
│ ├── topic_store.go
│ ├── user.go
│ ├── user_cache.go
│ ├── user_store.go
│ ├── utils.go
│ ├── weak_passwords.go
│ ├── websockets.go
│ ├── widget.go
│ ├── widget_search_and_filter.go
│ ├── widget_store.go
│ ├── widget_wol.go
│ ├── widget_wol_context.go
│ ├── widgets.go
│ ├── word_filters.go
│ ├── ws_hub.go
│ └── ws_user.go
├── config/
│ ├── config_example.json
│ ├── emoji_default.json
│ ├── filler.txt
│ └── weakpass_default.json
├── database.go
├── dev-update-linux
├── dev-update-travis
├── dev-update.bat
├── docs/
│ ├── configuration.md
│ ├── custom_pages.md
│ ├── emoji.md
│ ├── installation.md
│ ├── internationalisation.md
│ ├── landing_page.md
│ ├── templates.md
│ ├── updating.md
│ └── weak_passwords.md
├── experimental/
│ ├── config.json
│ ├── counterTree/
│ │ ├── tree.go
│ │ └── tree_test.go
│ ├── module_lua.go
│ ├── module_v8js.go
│ ├── new-replybit.html
│ ├── new-update.bat
│ ├── plugin_geoip.go
│ ├── plugin_sendmail.go
│ ├── theme-ext.json
│ └── theme-ext.xml
├── extend/
│ ├── adventure/
│ │ ├── lib/
│ │ │ ├── adventure.go
│ │ │ └── adventure_store.go
│ │ ├── plugin.json
│ │ └── prebuild/
│ │ └── filler.txt
│ ├── filler.go
│ ├── guilds/
│ │ ├── lib/
│ │ │ ├── guild_store.go
│ │ │ └── guilds.go
│ │ ├── plugin.json
│ │ ├── plugin_guilds.go
│ │ └── prebuild/
│ │ └── filler.txt
│ ├── heytherejs/
│ │ ├── main.js
│ │ └── plugin.json
│ ├── plugin_adventure.go
│ ├── plugin_bbcode.go
│ ├── plugin_heythere.go
│ ├── plugin_hyperdrive.go
│ ├── plugin_markdown.go
│ └── plugin_skeleton.go
├── gen_mssql.go
├── gen_mysql.go
├── gen_pgsql.go
├── gen_router.go
├── gen_tables.go
├── general_test.go
├── go.mod
├── go.sum
├── gosora_example.service
├── install/
│ ├── install.go
│ ├── mssql.go
│ ├── mysql.go
│ ├── pgsql.go
│ └── utils.go
├── install-docker
├── install-linux
├── install.bat
├── langs/
│ └── english.json
├── last_version.txt
├── logs/
│ └── filler.txt
├── main.go
├── migrations/
│ └── filler.txt
├── misc_test.go
├── mssql.go
├── mysql.go
├── old_router.go
├── pages/
│ └── page_test.html
├── parser_test.go
├── patcher/
│ ├── main.go
│ ├── patches.go
│ └── utils.go
├── pgsql.go
├── plugin_test.go
├── pre-run-linux
├── public/
│ ├── EQCSS.js
│ ├── Sortable-1.4.0/
│ │ ├── .editorconfig
│ │ ├── .gitignore
│ │ ├── .jshintrc
│ │ ├── CONTRIBUTING.md
│ │ ├── README.md
│ │ ├── Sortable.js
│ │ ├── bower.json
│ │ ├── component.json
│ │ ├── jquery.binding.js
│ │ └── package.json
│ ├── account.js
│ ├── analytics.js
│ ├── chartist/
│ │ ├── chartist-plugin-legend.css
│ │ └── chartist.css
│ ├── convo.js
│ ├── font-awesome-4.7.0/
│ │ └── fonts/
│ │ └── FontAwesome.otf
│ ├── global.js
│ ├── init.js
│ ├── jquery-emojiarea/
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── emojis.js
│ │ ├── jquery.emojiarea.css
│ │ └── jquery.emojiarea.js
│ ├── member.js
│ ├── panel_forum_edit.js
│ ├── panel_forums.js
│ ├── panel_menu_items.js
│ ├── profile_member.js
│ ├── register.js
│ ├── templates/
│ │ └── filler.txt
│ ├── trumbowyg/
│ │ └── ui/
│ │ ├── trumbowyg.css
│ │ └── trumbowyg.custom.css
│ └── widgets.js
├── pubnot/
│ ├── chartist/
│ │ ├── chartist-plugin-legend.css
│ │ ├── chartist.css
│ │ ├── chartist.js
│ │ └── scss/
│ │ ├── chartist.scss
│ │ └── settings/
│ │ └── _chartist-settings.scss
│ ├── font-awesome-4.7.0/
│ │ ├── css/
│ │ │ └── font-awesome.css
│ │ └── fonts/
│ │ └── FontAwesome.otf
│ └── trumbowyg/
│ ├── plugins/
│ │ ├── base64/
│ │ │ └── trumbowyg.base64.js
│ │ ├── cleanpaste/
│ │ │ └── trumbowyg.cleanpaste.js
│ │ ├── colors/
│ │ │ ├── trumbowyg.colors.js
│ │ │ └── ui/
│ │ │ ├── sass/
│ │ │ │ └── trumbowyg.colors.scss
│ │ │ └── trumbowyg.colors.css
│ │ ├── emoji/
│ │ │ ├── trumbowyg.emoji.js
│ │ │ └── ui/
│ │ │ ├── sass/
│ │ │ │ └── trumbowyg.emoji.scss
│ │ │ └── trumbowyg.emoji.css
│ │ ├── insertaudio/
│ │ │ └── trumbowyg.insertaudio.js
│ │ ├── noembed/
│ │ │ └── trumbowyg.noembed.js
│ │ ├── pasteimage/
│ │ │ └── trumbowyg.pasteimage.js
│ │ ├── preformatted/
│ │ │ └── trumbowyg.preformatted.js
│ │ ├── table/
│ │ │ └── trumbowyg.table.js
│ │ ├── template/
│ │ │ └── trumbowyg.template.js
│ │ └── upload/
│ │ └── trumbowyg.upload.js
│ ├── trumbowyg.js
│ └── ui/
│ ├── sass/
│ │ └── trumbowyg.scss
│ ├── trumbowyg.css
│ └── trumbowyg.custom.css
├── query_gen/
│ ├── acc_builders.go
│ ├── accumulator.go
│ ├── builder.go
│ ├── install.go
│ ├── micro_builders.go
│ ├── mssql.go
│ ├── mysql.go
│ ├── pgsql.go
│ ├── querygen.go
│ ├── transaction.go
│ ├── utils.go
│ └── utils_test.go
├── quick-update-linux
├── rev_templates.go
├── router.go
├── router_gen/
│ ├── build.bat
│ ├── main.go
│ ├── misc_test.go
│ ├── prec.go
│ ├── route_group.go
│ ├── route_impl.go
│ ├── route_subset.go
│ ├── router.go
│ ├── routes.go
│ └── run.bat
├── routes/
│ ├── account.go
│ ├── api.go
│ ├── attachments.go
│ ├── common.go
│ ├── convos.go
│ ├── forum.go
│ ├── forum_list.go
│ ├── misc.go
│ ├── moderate.go
│ ├── panel/
│ │ ├── analytics.go
│ │ ├── backups.go
│ │ ├── common.go
│ │ ├── dashboard.go
│ │ ├── debug.go
│ │ ├── forums.go
│ │ ├── groups.go
│ │ ├── logs.go
│ │ ├── pages.go
│ │ ├── plugins.go
│ │ ├── settings.go
│ │ ├── themes.go
│ │ ├── users.go
│ │ └── word_filters.go
│ ├── poll.go
│ ├── profile.go
│ ├── profile_reply.go
│ ├── reply.go
│ ├── reports.go
│ ├── stubs.go
│ ├── topic.go
│ ├── topic_list.go
│ └── user.go
├── routes.go
├── run-linux
├── run-linux-nowebsockets
├── run-linux-tests
├── run-nowebsockets.bat
├── run.bat
├── run_mssql.bat
├── run_tests.bat
├── run_tests_mssql.bat
├── schema/
│ ├── mssql/
│ │ ├── inserts.sql
│ │ ├── query_activity_stream.sql
│ │ ├── query_activity_stream_matches.sql
│ │ ├── query_activity_subscriptions.sql
│ │ ├── query_administration_logs.sql
│ │ ├── query_attachments.sql
│ │ ├── query_conversations.sql
│ │ ├── query_conversations_participants.sql
│ │ ├── query_conversations_posts.sql
│ │ ├── query_emails.sql
│ │ ├── query_forums.sql
│ │ ├── query_forums_actions.sql
│ │ ├── query_forums_permissions.sql
│ │ ├── query_likes.sql
│ │ ├── query_login_logs.sql
│ │ ├── query_memchunks.sql
│ │ ├── query_menu_items.sql
│ │ ├── query_menus.sql
│ │ ├── query_meta.sql
│ │ ├── query_moderation_logs.sql
│ │ ├── query_pages.sql
│ │ ├── query_password_resets.sql
│ │ ├── query_perfchunks.sql
│ │ ├── query_plugins.sql
│ │ ├── query_polls.sql
│ │ ├── query_polls_options.sql
│ │ ├── query_polls_voters.sql
│ │ ├── query_polls_votes.sql
│ │ ├── query_postchunks.sql
│ │ ├── query_registration_logs.sql
│ │ ├── query_replies.sql
│ │ ├── query_revisions.sql
│ │ ├── query_settings.sql
│ │ ├── query_sync.sql
│ │ ├── query_themes.sql
│ │ ├── query_topicchunks.sql
│ │ ├── query_topics.sql
│ │ ├── query_updates.sql
│ │ ├── query_users.sql
│ │ ├── query_users_2fa_keys.sql
│ │ ├── query_users_avatar_queue.sql
│ │ ├── query_users_blocks.sql
│ │ ├── query_users_groups.sql
│ │ ├── query_users_groups_promotions.sql
│ │ ├── query_users_groups_scheduler.sql
│ │ ├── query_users_replies.sql
│ │ ├── query_viewchunks.sql
│ │ ├── query_viewchunks_agents.sql
│ │ ├── query_viewchunks_forums.sql
│ │ ├── query_viewchunks_langs.sql
│ │ ├── query_viewchunks_referrers.sql
│ │ ├── query_viewchunks_systems.sql
│ │ ├── query_widgets.sql
│ │ └── query_word_filters.sql
│ ├── mysql/
│ │ ├── inserts.sql
│ │ ├── query_activity_stream.sql
│ │ ├── query_activity_stream_matches.sql
│ │ ├── query_activity_subscriptions.sql
│ │ ├── query_administration_logs.sql
│ │ ├── query_attachments.sql
│ │ ├── query_conversations.sql
│ │ ├── query_conversations_participants.sql
│ │ ├── query_conversations_posts.sql
│ │ ├── query_emails.sql
│ │ ├── query_forums.sql
│ │ ├── query_forums_actions.sql
│ │ ├── query_forums_permissions.sql
│ │ ├── query_likes.sql
│ │ ├── query_login_logs.sql
│ │ ├── query_memchunks.sql
│ │ ├── query_menu_items.sql
│ │ ├── query_menus.sql
│ │ ├── query_meta.sql
│ │ ├── query_moderation_logs.sql
│ │ ├── query_pages.sql
│ │ ├── query_password_resets.sql
│ │ ├── query_perfchunks.sql
│ │ ├── query_plugins.sql
│ │ ├── query_polls.sql
│ │ ├── query_polls_options.sql
│ │ ├── query_polls_voters.sql
│ │ ├── query_polls_votes.sql
│ │ ├── query_postchunks.sql
│ │ ├── query_registration_logs.sql
│ │ ├── query_replies.sql
│ │ ├── query_revisions.sql
│ │ ├── query_settings.sql
│ │ ├── query_sync.sql
│ │ ├── query_themes.sql
│ │ ├── query_topicchunks.sql
│ │ ├── query_topics.sql
│ │ ├── query_updates.sql
│ │ ├── query_users.sql
│ │ ├── query_users_2fa_keys.sql
│ │ ├── query_users_avatar_queue.sql
│ │ ├── query_users_blocks.sql
│ │ ├── query_users_groups.sql
│ │ ├── query_users_groups_promotions.sql
│ │ ├── query_users_groups_scheduler.sql
│ │ ├── query_users_replies.sql
│ │ ├── query_viewchunks.sql
│ │ ├── query_viewchunks_agents.sql
│ │ ├── query_viewchunks_forums.sql
│ │ ├── query_viewchunks_langs.sql
│ │ ├── query_viewchunks_referrers.sql
│ │ ├── query_viewchunks_systems.sql
│ │ ├── query_widgets.sql
│ │ └── query_word_filters.sql
│ ├── pgsql/
│ │ ├── inserts.sql
│ │ ├── query_activity_stream.sql
│ │ ├── query_activity_stream_matches.sql
│ │ ├── query_activity_subscriptions.sql
│ │ ├── query_administration_logs.sql
│ │ ├── query_attachments.sql
│ │ ├── query_conversations.sql
│ │ ├── query_conversations_participants.sql
│ │ ├── query_conversations_posts.sql
│ │ ├── query_emails.sql
│ │ ├── query_forums.sql
│ │ ├── query_forums_actions.sql
│ │ ├── query_forums_permissions.sql
│ │ ├── query_likes.sql
│ │ ├── query_login_logs.sql
│ │ ├── query_memchunks.sql
│ │ ├── query_menu_items.sql
│ │ ├── query_menus.sql
│ │ ├── query_meta.sql
│ │ ├── query_moderation_logs.sql
│ │ ├── query_pages.sql
│ │ ├── query_password_resets.sql
│ │ ├── query_perfchunks.sql
│ │ ├── query_plugins.sql
│ │ ├── query_polls.sql
│ │ ├── query_polls_options.sql
│ │ ├── query_polls_votes.sql
│ │ ├── query_postchunks.sql
│ │ ├── query_registration_logs.sql
│ │ ├── query_replies.sql
│ │ ├── query_revisions.sql
│ │ ├── query_settings.sql
│ │ ├── query_sync.sql
│ │ ├── query_themes.sql
│ │ ├── query_topicchunks.sql
│ │ ├── query_topics.sql
│ │ ├── query_updates.sql
│ │ ├── query_users.sql
│ │ ├── query_users_2fa_keys.sql
│ │ ├── query_users_avatar_queue.sql
│ │ ├── query_users_blocks.sql
│ │ ├── query_users_groups.sql
│ │ ├── query_users_groups_promotions.sql
│ │ ├── query_users_groups_scheduler.sql
│ │ ├── query_users_replies.sql
│ │ ├── query_viewchunks.sql
│ │ ├── query_viewchunks_agents.sql
│ │ ├── query_viewchunks_forums.sql
│ │ ├── query_viewchunks_langs.sql
│ │ ├── query_viewchunks_referrers.sql
│ │ ├── query_viewchunks_systems.sql
│ │ ├── query_widgets.sql
│ │ └── query_word_filters.sql
│ └── schema.json
├── templates/
│ ├── account.html
│ ├── account_blocked.html
│ ├── account_logins.html
│ ├── account_menu.html
│ ├── account_own_edit.html
│ ├── account_own_edit_email.html
│ ├── account_own_edit_level.html
│ ├── account_own_edit_mfa.html
│ ├── account_own_edit_mfa_setup.html
│ ├── account_own_edit_password.html
│ ├── account_own_edit_privacy.html
│ ├── account_test.html
│ ├── alert.html
│ ├── are_you_sure.html
│ ├── convo.html
│ ├── convo_row.html
│ ├── convo_row_alt.html
│ ├── convos.html
│ ├── create_convo.html
│ ├── create_topic.html
│ ├── custom_page.html
│ ├── error.html
│ ├── footer.html
│ ├── forum.html
│ ├── forum_gallery.html
│ ├── forums.html
│ ├── guilds_create_guild.html
│ ├── guilds_css.html
│ ├── guilds_guild_list.html
│ ├── guilds_member_list.html
│ ├── guilds_view_guild.html
│ ├── header.html
│ ├── ip_search.html
│ ├── level_list.html
│ ├── login.html
│ ├── login_mfa_verify.html
│ ├── menu_alerts.html
│ ├── menu_item.html
│ ├── notice.html
│ ├── overrides/
│ │ └── filler.txt
│ ├── overview.html
│ ├── paginator.html
│ ├── paginator_mod.html
│ ├── panel.html
│ ├── panel_adminlogs.html
│ ├── panel_analytics_active_memory.html
│ ├── panel_analytics_agent_views.html
│ ├── panel_analytics_agents.html
│ ├── panel_analytics_forum_views.html
│ ├── panel_analytics_forums.html
│ ├── panel_analytics_lang_views.html
│ ├── panel_analytics_langs.html
│ ├── panel_analytics_memory.html
│ ├── panel_analytics_performance.html
│ ├── panel_analytics_posts.html
│ ├── panel_analytics_referrer_views.html
│ ├── panel_analytics_referrers.html
│ ├── panel_analytics_route_views.html
│ ├── panel_analytics_routes.html
│ ├── panel_analytics_routes_perf.html
│ ├── panel_analytics_script.html
│ ├── panel_analytics_script_memory.html
│ ├── panel_analytics_script_perf.html
│ ├── panel_analytics_system_views.html
│ ├── panel_analytics_systems.html
│ ├── panel_analytics_time_range.html
│ ├── panel_analytics_time_range_month.html
│ ├── panel_analytics_topics.html
│ ├── panel_analytics_views.html
│ ├── panel_are_you_sure.html
│ ├── panel_backups.html
│ ├── panel_before_head.html
│ ├── panel_dashboard.html
│ ├── panel_debug.html
│ ├── panel_debug_stat.html
│ ├── panel_debug_stat_head.html
│ ├── panel_debug_stat_head_q.html
│ ├── panel_debug_stat_q.html
│ ├── panel_debug_subhead.html
│ ├── panel_forum_edit.html
│ ├── panel_forum_edit_perms.html
│ ├── panel_forums.html
│ ├── panel_group_edit.html
│ ├── panel_group_edit_perms.html
│ ├── panel_group_edit_promotions.html
│ ├── panel_group_menu.html
│ ├── panel_groups.html
│ ├── panel_inner_menu.html
│ ├── panel_menu.html
│ ├── panel_modlogs.html
│ ├── panel_pages.html
│ ├── panel_pages_edit.html
│ ├── panel_plugins.html
│ ├── panel_reglogs.html
│ ├── panel_setting.html
│ ├── panel_settings.html
│ ├── panel_themes.html
│ ├── panel_themes_menus.html
│ ├── panel_themes_menus_item_edit.html
│ ├── panel_themes_menus_items.html
│ ├── panel_themes_widgets.html
│ ├── panel_themes_widgets_widget.html
│ ├── panel_user_edit.html
│ ├── panel_users.html
│ ├── panel_word_filters.html
│ ├── password_reset.html
│ ├── password_reset_token.html
│ ├── profile.html
│ ├── profile_comments_row.html
│ ├── profile_comments_row_alt.html
│ ├── register.html
│ ├── register_verify.html
│ ├── topic.html
│ ├── topic_alt.html
│ ├── topic_alt_inner.html
│ ├── topic_alt_mini.html
│ ├── topic_alt_poll.html
│ ├── topic_alt_posts.html
│ ├── topic_alt_quick_reply.html
│ ├── topic_alt_userinfo.html
│ ├── topic_c_attach_item.html
│ ├── topic_c_edit_post.html
│ ├── topic_c_poll_input.html
│ ├── topic_inner.html
│ ├── topic_mini.html
│ ├── topic_poll.html
│ ├── topic_posts.html
│ ├── topics.html
│ ├── topics_inner.html
│ ├── topics_mini.html
│ ├── topics_mod_floater.html
│ ├── topics_quick_topic.html
│ ├── topics_topic.html
│ ├── widget_about.html
│ ├── widget_menu.html
│ ├── widget_online.html
│ ├── widget_search_and_filter.html
│ └── widget_simple.html
├── themes/
│ ├── cosora/
│ │ ├── public/
│ │ │ ├── account.css
│ │ │ ├── convo.css
│ │ │ ├── main.css
│ │ │ ├── misc.js
│ │ │ ├── panel.css
│ │ │ └── profile.css
│ │ └── theme.json
│ ├── nox/
│ │ ├── overrides/
│ │ │ ├── login.html
│ │ │ ├── panel_before_head.html
│ │ │ ├── panel_group_menu.html
│ │ │ ├── panel_inner_menu.html
│ │ │ ├── panel_menu.html
│ │ │ ├── profile_comments_row.html
│ │ │ └── topics_topic.html
│ │ ├── public/
│ │ │ ├── acc_panel_common.css
│ │ │ ├── account.css
│ │ │ ├── convo.css
│ │ │ ├── fa-svg/
│ │ │ │ ├── LICENSE.txt
│ │ │ │ └── README.md
│ │ │ ├── main.css
│ │ │ ├── misc.js
│ │ │ ├── panel.css
│ │ │ └── profile.css
│ │ └── theme.json
│ ├── shadow/
│ │ ├── DEVELOPERS.md
│ │ ├── overrides/
│ │ │ └── login.html
│ │ ├── public/
│ │ │ ├── account.css
│ │ │ ├── convo.css
│ │ │ ├── main.css
│ │ │ ├── misc.js
│ │ │ ├── panel.css
│ │ │ └── profile.css
│ │ └── theme.json
│ └── tempra_simple/
│ ├── DEVELOPERS.md
│ ├── overrides/
│ │ └── login.html
│ ├── public/
│ │ ├── account.css
│ │ ├── convo.css
│ │ ├── main.css
│ │ ├── media.partial.css
│ │ ├── misc.js
│ │ ├── panel.css
│ │ ├── profile.css
│ │ └── sample.css
│ └── theme.json
├── tickloop.go
├── tmp/
│ └── filler.txt
├── tmpl_client/
│ └── stub.go
├── tmplstub.go
├── update-deps-linux
├── update-deps.bat
├── updater/
│ └── main.go
├── uploads/
│ └── filler.txt
└── uutils/
└── utils.go
Showing preview only (281K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3142 symbols across 382 files)
FILE: cmd/common_hook_gen/hookgen.go
type HookVars (line 10) | type HookVars struct
type Hook (line 15) | type Hook struct
function AddHooks (line 28) | func AddHooks(add func(name, params, ret, htype string, multiHook, skip ...
function Write (line 70) | func Write(hookVars HookVars) {
function writeFile (line 103) | func writeFile(name, body string) error {
FILE: cmd/elasticsearch/setup.go
function main (line 18) | func main() {
function prepMySQL (line 60) | func prepMySQL() error {
type ESIndexBase (line 71) | type ESIndexBase struct
type ESIndexMappings (line 75) | type ESIndexMappings struct
type ESIndexDoc (line 79) | type ESIndexDoc struct
type ESDocMap (line 83) | type ESDocMap
method Add (line 85) | func (d ESDocMap) Add(column string, cType string) {
function setupIndices (line 89) | func setupIndices(client *elastic.Client) error {
type ESTopic (line 161) | type ESTopic struct
type ESReply (line 169) | type ESReply struct
function setupData (line 177) | func setupData(client *elastic.Client) error {
FILE: cmd/hook_gen/main.go
function main (line 17) | func main() {
FILE: cmd/hook_stub_gen/main.go
function main (line 13) | func main() {
FILE: cmd/install/install.go
function main (line 37) | func main() {
function abortError (line 179) | func abortError(err error) {
function handleDatabaseDetails (line 185) | func handleDatabaseDetails() (adap install.InstallAdapter, ok bool) {
function getSiteDetails (line 257) | func getSiteDetails() bool {
function obfuscatePassword (line 307) | func obfuscatePassword(password string) (out string) {
function pressAnyKey (line 314) | func pressAnyKey() {
FILE: cmd/query_gen/main.go
function main (line 18) | func main() {
function writeStatements (line 50) | func writeStatements(a qgen.Adapter) (err error) {
function seedTables (line 75) | func seedTables(a qgen.Adapter) error {
type LitStr (line 305) | type LitStr
function writeSelects (line 307) | func writeSelects(a qgen.Adapter) error {
function writeLeftJoins (line 321) | func writeLeftJoins(a qgen.Adapter) error {
function writeInnerJoins (line 327) | func writeInnerJoins(a qgen.Adapter) (err error) {
function writeInserts (line 331) | func writeInserts(a qgen.Adapter) error {
function writeUpdates (line 339) | func writeUpdates(a qgen.Adapter) error {
function writeDeletes (line 351) | func writeDeletes(a qgen.Adapter) error {
function writeSimpleCounts (line 362) | func writeSimpleCounts(a qgen.Adapter) error {
function writeInsertSelects (line 366) | func writeInsertSelects(a qgen.Adapter) error {
function writeInsertLeftJoins (line 386) | func writeInsertLeftJoins(a qgen.Adapter) error {
function writeInsertInnerJoins (line 390) | func writeInsertInnerJoins(a qgen.Adapter) error {
function writeFile (line 394) | func writeFile(name, content string) (err error) {
FILE: cmd/query_gen/spitter.go
type PrimaryKeySpitter (line 6) | type PrimaryKeySpitter struct
method Hook (line 14) | func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) ...
method Write (line 35) | func (spit *PrimaryKeySpitter) Write() error {
function NewPrimaryKeySpitter (line 10) | func NewPrimaryKeySpitter() *PrimaryKeySpitter {
FILE: cmd/query_gen/tables.go
function createTables (line 14) | func createTables(a qgen.Adapter) error {
function createTables2 (line 23) | func createTables2(a qgen.Adapter, f func(table, charset, collation stri...
FILE: common/activity_stream.go
type ActivityStream (line 11) | type ActivityStream interface
type DefaultActivityStream (line 22) | type DefaultActivityStream struct
method Add (line 48) | func (s *DefaultActivityStream) Add(a Alert) (int, error) {
method Get (line 57) | func (s *DefaultActivityStream) Get(id int) (Alert, error) {
method Delete (line 63) | func (s *DefaultActivityStream) Delete(id int) error {
method DeleteByParams (line 68) | func (s *DefaultActivityStream) DeleteByParams(event string, elementID...
method DeleteByParamsExtra (line 73) | func (s *DefaultActivityStream) DeleteByParamsExtra(event string, elem...
method AidsByParams (line 78) | func (s *DefaultActivityStream) AidsByParams(event string, elementID i...
method AidsByParamsExtra (line 94) | func (s *DefaultActivityStream) AidsByParamsExtra(event string, elemen...
method Count (line 111) | func (s *DefaultActivityStream) Count() (count int) {
function NewDefaultActivityStream (line 33) | func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivitySt...
FILE: common/activity_stream_matches.go
type ActivityStreamMatches (line 11) | type ActivityStreamMatches interface
type DefaultActivityStreamMatches (line 18) | type DefaultActivityStreamMatches struct
method Add (line 33) | func (s *DefaultActivityStreamMatches) Add(watcher, asid int) error {
method Delete (line 38) | func (s *DefaultActivityStreamMatches) Delete(watcher, asid int) error {
method DeleteAndCountChanged (line 43) | func (s *DefaultActivityStreamMatches) DeleteAndCountChanged(watcher, ...
method CountAsid (line 52) | func (s *DefaultActivityStreamMatches) CountAsid(asid int) int {
function NewDefaultActivityStreamMatches (line 24) | func NewDefaultActivityStreamMatches(acc *qgen.Accumulator) (*DefaultAct...
FILE: common/alerts.go
type Alert (line 22) | type Alert struct
type AlertStmts (line 35) | type AlertStmts struct
function init (line 44) | func init() {
constant AlertsGrowHint (line 57) | AlertsGrowHint = len(`{"msgs":[],"count":,"tc":}`) + 1 + 10
function escapeTextInJson (line 60) | func escapeTextInJson(in string) string {
function BuildAlert (line 65) | func BuildAlert(a Alert, user User /* The current user */) (out string, ...
function buildAlertString (line 163) | func buildAlertString(msg string, sub []string, path, avatar string, asi...
constant AlertsGrowHint2 (line 169) | AlertsGrowHint2 = len(`{"msg":"","sub":[],"path":"","img":"","id":}`) + ...
function buildAlertSb (line 172) | func buildAlertSb(sb *strings.Builder, msg string, sub []string, path, a...
function BuildAlertSb (line 194) | func BuildAlertSb(sb *strings.Builder, a *Alert, u *User /* The current ...
function AddActivityAndNotifyAll (line 304) | func AddActivityAndNotifyAll(a Alert) error {
function AddActivityAndNotifyTarget (line 313) | func AddActivityAndNotifyTarget(a Alert) error {
function NotifyWatchers (line 337) | func NotifyWatchers(asid int) error {
function notifyWatchers (line 353) | func notifyWatchers(asid int) {
function DismissAlert (line 383) | func DismissAlert(uid, aid int) {
FILE: common/alerts/tmpls.go
type AlertItem (line 5) | type AlertItem struct
FILE: common/analytics.go
type AnalyticsTimeRange (line 13) | type AnalyticsTimeRange struct
type AnalyticsStore (line 21) | type AnalyticsStore interface
type DefaultAnalytics (line 25) | type DefaultAnalytics struct
method FillViewMap (line 43) | func (s *DefaultAnalytics) FillViewMap(tbl string, tr *AnalyticsTimeRa...
function NewDefaultAnalytics (line 28) | func NewDefaultAnalytics() *DefaultAnalytics {
function AnalyticsTimeRangeToLabelList (line 56) | func AnalyticsTimeRangeToLabelList(tr *AnalyticsTimeRange) (revLabelList...
function AnalyticsRowsToViewMap (line 68) | func AnalyticsRowsToViewMap(rows *sql.Rows, labelList []int64, viewMap m...
FILE: common/attachments.go
type MiniAttachment (line 19) | type MiniAttachment struct
type Attachment (line 31) | type Attachment struct
type AttachmentStore (line 45) | type AttachmentStore interface
type DefaultAttachmentStore (line 63) | type DefaultAttachmentStore struct
method GetForRenderRoute (line 105) | func (s *DefaultAttachmentStore) GetForRenderRoute(filename string, si...
method MiniGetList (line 112) | func (s *DefaultAttachmentStore) MiniGetList(originTable string, origi...
method BulkMiniGetList (line 137) | func (s *DefaultAttachmentStore) BulkMiniGetList(originTable string, i...
method FGet (line 180) | func (s *DefaultAttachmentStore) FGet(id int) (*Attachment, error) {
method Get (line 194) | func (s *DefaultAttachmentStore) Get(id int) (*MiniAttachment, error) {
method Add (line 208) | func (s *DefaultAttachmentStore) Add(sectionID int, sectionTable strin...
method MoveTo (line 217) | func (s *DefaultAttachmentStore) MoveTo(sectionID, originID int, origi...
method MoveToByExtra (line 222) | func (s *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTa...
method Count (line 227) | func (s *DefaultAttachmentStore) Count() (count int) {
method CountIn (line 235) | func (s *DefaultAttachmentStore) CountIn(originTable string, oid int) ...
method CountInPath (line 243) | func (s *DefaultAttachmentStore) CountInPath(path string) (count int) {
method Delete (line 251) | func (s *DefaultAttachmentStore) Delete(id int) error {
method AddLinked (line 257) | func (s *DefaultAttachmentStore) AddLinked(otable string, oid int) (er...
method RemoveLinked (line 279) | func (s *DefaultAttachmentStore) RemoveLinked(otable string, oid int) ...
function NewDefaultAttachmentStore (line 81) | func NewDefaultAttachmentStore(acc *qgen.Accumulator) (*DefaultAttachmen...
function DeleteAttachment (line 300) | func DeleteAttachment(aid int) error {
function deleteAttachment (line 313) | func deleteAttachment(a *Attachment) error {
FILE: common/audit_logs.go
type LogItem (line 13) | type LogItem struct
type LogStore (line 23) | type LogStore interface
type SQLModLogStore (line 30) | type SQLModLogStore struct
method Create (line 48) | func (s *SQLModLogStore) Create(action string, elementID int, elementT...
method CreateExtra (line 52) | func (s *SQLModLogStore) CreateExtra(action string, elementID int, ele...
method Count (line 57) | func (s *SQLModLogStore) Count() (count int) {
method GetOffset (line 79) | func (s *SQLModLogStore) GetOffset(offset, perPage int) (logs []LogIte...
function NewModLogStore (line 36) | func NewModLogStore(acc *qgen.Accumulator) (*SQLModLogStore, error) {
function buildLogList (line 65) | func buildLogList(rows *sql.Rows) (logs []LogItem, err error) {
type SQLAdminLogStore (line 88) | type SQLAdminLogStore struct
method Create (line 105) | func (s *SQLAdminLogStore) Create(action string, elementID int, elemen...
method CreateExtra (line 109) | func (s *SQLAdminLogStore) CreateExtra(action string, elementID int, e...
method Count (line 114) | func (s *SQLAdminLogStore) Count() (count int) {
method GetOffset (line 122) | func (s *SQLAdminLogStore) GetOffset(offset, perPage int) (logs []LogI...
function NewAdminLogStore (line 94) | func NewAdminLogStore(acc *qgen.Accumulator) (*SQLAdminLogStore, error) {
FILE: common/auth.go
constant SaltLength (line 29) | SaltLength int = 32
constant SessionLength (line 30) | SessionLength int = 80
type AuthInt (line 68) | type AuthInt interface
type DefaultAuth (line 82) | type DefaultAuth struct
method Authenticate (line 101) | func (auth *DefaultAuth) Authenticate(name, password string) (uid int,...
method ValidateMFAToken (line 131) | func (auth *DefaultAuth) ValidateMFAToken(mfaToken string, uid int) er...
method ForceLogout (line 164) | func (auth *DefaultAuth) ForceLogout(uid int) error {
method Logout (line 196) | func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
method SetCookies (line 205) | func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, se...
method SetProvisionalCookies (line 214) | func (auth *DefaultAuth) SetProvisionalCookies(w http.ResponseWriter, ...
method GetCookies (line 224) | func (auth *DefaultAuth) GetCookies(r *http.Request) (uid int, session...
method SessionCheck (line 242) | func (auth *DefaultAuth) SessionCheck(w http.ResponseWriter, r *http.R...
method CreateSession (line 266) | func (auth *DefaultAuth) CreateSession(uid int) (session string, err e...
method CreateProvisionalSession (line 285) | func (auth *DefaultAuth) CreateProvisionalSession(uid int) (provSessio...
function NewDefaultAuth (line 89) | func NewDefaultAuth() (*DefaultAuth, error) {
function setCookie (line 178) | func setCookie(w http.ResponseWriter, cookie *http.Cookie, sameSite stri...
function deleteCookie (line 190) | func deleteCookie(w http.ResponseWriter, cookie *http.Cookie) {
function CheckPassword (line 298) | func CheckPassword(realPassword, password, salt string) (err error) {
function GeneratePassword (line 312) | func GeneratePassword(password string) (hash, salt string, err error) {
function BcryptCheckPassword (line 320) | func BcryptCheckPassword(realPassword, password, salt string) (err error) {
function BcryptGeneratePassword (line 325) | func BcryptGeneratePassword(password string) (hash, salt string, err err...
function FriendlyGAuthSecret (line 371) | func FriendlyGAuthSecret(secret string) (out string) {
function GenerateGAuthSecret (line 380) | func GenerateGAuthSecret() (string, error) {
function VerifyGAuthToken (line 383) | func VerifyGAuthToken(secret, token string) (bool, error) {
FILE: common/cache.go
type DataStore (line 13) | type DataStore interface
type DataCache (line 21) | type DataCache interface
FILE: common/common.go
constant Hour (line 31) | Hour int = 60 * 60
constant Day (line 32) | Day = Hour * 24
constant Week (line 33) | Week = Day * 7
constant Month (line 34) | Month = Day * 30
constant Year (line 35) | Year = Day * 365
constant Kilobyte (line 36) | Kilobyte int = 1024
constant Megabyte (line 37) | Megabyte = Kilobyte * 1024
constant Gigabyte (line 38) | Gigabyte = Megabyte * 1024
constant Terabyte (line 39) | Terabyte = Gigabyte * 1024
constant Petabyte (line 40) | Petabyte = Terabyte * 1024
type StringList (line 69) | type StringList
method Contains (line 118) | func (sl StringList) Contains(needle string) bool {
function init (line 111) | func init() {
type dbInits (line 148) | type dbInits
method Run (line 152) | func (inits dbInits) Run() error {
method Add (line 161) | func (inits dbInits) Add(i ...func(acc *qgen.Accumulator) error) {
function StoppedServer (line 166) | func StoppedServer(msg ...interface{}) {
function DebugDetail (line 177) | func DebugDetail(args ...interface{}) {
function DebugDetailf (line 183) | func DebugDetailf(str string, args ...interface{}) {
function DebugLog (line 189) | func DebugLog(args ...interface{}) {
function DebugLogf (line 195) | func DebugLogf(str string, args ...interface{}) {
function Log (line 201) | func Log(args ...interface{}) {
function Logf (line 204) | func Logf(str string, args ...interface{}) {
function Err (line 207) | func Err(args ...interface{}) {
function Count (line 211) | func Count(stmt *sql.Stmt) (count int) {
function Countf (line 218) | func Countf(stmt *sql.Stmt, args ...interface{}) (count int) {
function Createf (line 225) | func Createf(stmt *sql.Stmt, args ...interface{}) (id int, e error) {
function eachall (line 234) | func eachall(stmt *sql.Stmt, f func(r *sql.Rows) error) error {
function inqbuild (line 250) | func inqbuild(ids []int) ([]interface{}, string) {
function inqbuild2 (line 273) | func inqbuild2(count int) string {
function inqbuildstr (line 289) | func inqbuildstr(strs []string) ([]interface{}, string) {
type ConnWatcher (line 314) | type ConnWatcher struct
method StateChange (line 318) | func (cw *ConnWatcher) StateChange(conn net.Conn, state http.ConnState) {
method Count (line 327) | func (cw *ConnWatcher) Count() int {
function EatPanics (line 331) | func EatPanics() {
FILE: common/conversations.go
type ConvoStmts (line 18) | type ConvoStmts struct
function init (line 34) | func init() {
type Conversation (line 55) | type Conversation struct
method Posts (line 64) | func (co *Conversation) Posts(offset, itemsPerPage int) (posts []*Conv...
method PostsCount (line 87) | func (co *Conversation) PostsCount() (count int) {
method Uids (line 91) | func (co *Conversation) Uids() (ids []int, err error) {
method Has (line 107) | func (co *Conversation) Has(uid int) (in bool) {
method Update (line 111) | func (co *Conversation) Update() error {
method Create (line 116) | func (co *Conversation) Create() (int, error) {
function BuildConvoURL (line 126) | func BuildConvoURL(coid int) string {
type ConversationExtra (line 130) | type ConversationExtra struct
type ConversationStore (line 135) | type ConversationStore interface
type DefaultConversationStore (line 145) | type DefaultConversationStore struct
method Get (line 172) | func (s *DefaultConversationStore) Get(id int) (*Conversation, error) {
method GetUser (line 179) | func (s *DefaultConversationStore) GetUser(uid, offset int) (cos []*Co...
method GetUserExtra (line 205) | func (s *DefaultConversationStore) GetUserExtra(uid, offset int) (cos ...
method GetUserCount (line 306) | func (s *DefaultConversationStore) GetUserCount(uid int) (count int) {
method Delete (line 315) | func (s *DefaultConversationStore) Delete(id int) error {
method Create (line 328) | func (s *DefaultConversationStore) Create(content string, createdBy in...
method Count (line 365) | func (s *DefaultConversationStore) Count() (count int) {
function NewDefaultConversationStore (line 157) | func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConvers...
FILE: common/convos_posts.go
type ConvoPostProcessor (line 13) | type ConvoPostProcessor interface
type DefaultConvoPostProcessor (line 18) | type DefaultConvoPostProcessor struct
method OnLoad (line 25) | func (pr *DefaultConvoPostProcessor) OnLoad(co *ConversationPost) (*Co...
method OnSave (line 29) | func (pr *DefaultConvoPostProcessor) OnSave(co *ConversationPost) (*Co...
function NewDefaultConvoPostProcessor (line 21) | func NewDefaultConvoPostProcessor() *DefaultConvoPostProcessor {
type AesConvoPostProcessor (line 33) | type AesConvoPostProcessor struct
method OnLoad (line 40) | func (pr *AesConvoPostProcessor) OnLoad(co *ConversationPost) (*Conver...
method OnSave (line 77) | func (pr *AesConvoPostProcessor) OnSave(co *ConversationPost) (*Conver...
function NewAesConvoPostProcessor (line 36) | func NewAesConvoPostProcessor() *AesConvoPostProcessor {
type ConversationPost (line 101) | type ConversationPost struct
method Fetch (line 110) | func (co *ConversationPost) Fetch() error {
method Update (line 114) | func (co *ConversationPost) Update() error {
method Create (line 124) | func (co *ConversationPost) Create() (int, error) {
method Delete (line 139) | func (co *ConversationPost) Delete() error {
FILE: common/counters/agents.go
type DefaultAgentViewCounter (line 14) | type DefaultAgentViewCounter struct
method Tick (line 30) | func (co *DefaultAgentViewCounter) Tick() error {
method insertChunk (line 41) | func (co *DefaultAgentViewCounter) insertChunk(count int64, agent int)...
method Bump (line 51) | func (co *DefaultAgentViewCounter) Bump(agent int) {
function NewDefaultAgentViewCounter (line 19) | func NewDefaultAgentViewCounter(acc *qgen.Accumulator) (*DefaultAgentVie...
FILE: common/counters/common.go
function SetRouteMapEnum (line 9) | func SetRouteMapEnum(rme map[string]int) {
function SetReverseRouteMapEnum (line 13) | func SetReverseRouteMapEnum(rrme map[int]string) {
function SetAgentMapEnum (line 20) | func SetAgentMapEnum(ame map[string]int) {
function SetReverseAgentMapEnum (line 24) | func SetReverseAgentMapEnum(rame map[int]string) {
function SetOSMapEnum (line 31) | func SetOSMapEnum(osme map[string]int) {
function SetReverseOSMapEnum (line 35) | func SetReverseOSMapEnum(rosme map[int]string) {
type RWMutexCounterBucket (line 39) | type RWMutexCounterBucket struct
type MutexCounterBucket (line 44) | type MutexCounterBucket struct
type MutexCounter64Bucket (line 49) | type MutexCounter64Bucket struct
FILE: common/counters/forums.go
type DefaultForumViewCounter (line 16) | type DefaultForumViewCounter struct
method Tick (line 38) | func (co *DefaultForumViewCounter) Tick() error {
method insertChunk (line 67) | func (co *DefaultForumViewCounter) insertChunk(count, forum int) error {
method Bump (line 76) | func (co *DefaultForumViewCounter) Bump(fid int) {
function NewDefaultForumViewCounter (line 25) | func NewDefaultForumViewCounter() (*DefaultForumViewCounter, error) {
FILE: common/counters/langs.go
type DefaultLangViewCounter (line 102) | type DefaultLangViewCounter struct
method Tick (line 127) | func (co *DefaultLangViewCounter) Tick() error {
method insertChunk (line 138) | func (co *DefaultLangViewCounter) insertChunk(count int64, id int) err...
method Bump (line 151) | func (co *DefaultLangViewCounter) Bump(langCode string) (validCode boo...
method Bump2 (line 170) | func (co *DefaultLangViewCounter) Bump2(id int) {
function NewDefaultLangViewCounter (line 110) | func NewDefaultLangViewCounter(acc *qgen.Accumulator) (*DefaultLangViewC...
FILE: common/counters/memory.go
type DefaultMemoryCounter (line 16) | type DefaultMemoryCounter struct
method Tick (line 58) | func (co *DefaultMemoryCounter) Tick() (e error) {
function NewMemoryCounter (line 29) | func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, err...
FILE: common/counters/performance.go
type PerfCounterBucket (line 15) | type PerfCounterBucket struct
type DefaultPerfCounter (line 22) | type DefaultPerfCounter struct
method Tick (line 46) | func (co *DefaultPerfCounter) Tick() error {
method insertChunk (line 73) | func (co *DefaultPerfCounter) insertChunk(low, high, avg int64) error {
method Push (line 85) | func (co *DefaultPerfCounter) Push(dur time.Duration /*,_ bool*/) {
function NewDefaultPerfCounter (line 28) | func NewDefaultPerfCounter(acc *qgen.Accumulator) (*DefaultPerfCounter, ...
FILE: common/counters/posts.go
type DefaultPostCounter (line 14) | type DefaultPostCounter struct
method Tick (line 33) | func (co *DefaultPostCounter) Tick() (err error) {
method Bump (line 52) | func (co *DefaultPostCounter) Bump() {
method insertChunk (line 56) | func (co *DefaultPostCounter) insertChunk(count int64) error {
function NewPostCounter (line 21) | func NewPostCounter() (*DefaultPostCounter, error) {
FILE: common/counters/referrers.go
type ReferrerItem (line 18) | type ReferrerItem struct
type DefaultReferrerTracker (line 24) | type DefaultReferrerTracker struct
method Tick (line 47) | func (ref *DefaultReferrerTracker) Tick() (err error) {
method insertChunk (line 84) | func (ref *DefaultReferrerTracker) insertChunk(referrer string, count ...
method Bump (line 93) | func (ref *DefaultReferrerTracker) Bump(referrer string) {
function NewDefaultReferrerTracker (line 33) | func NewDefaultReferrerTracker() (*DefaultReferrerTracker, error) {
FILE: common/counters/requests.go
type DefaultViewCounter (line 16) | type DefaultViewCounter struct
method Tick (line 35) | func (co *DefaultViewCounter) Tick() (err error) {
method Bump (line 54) | func (co *DefaultViewCounter) Bump() {
method insertChunk (line 58) | func (co *DefaultViewCounter) insertChunk(count int64) error {
function NewGlobalViewCounter (line 23) | func NewGlobalViewCounter(acc *qgen.Accumulator) (*DefaultViewCounter, e...
FILE: common/counters/routes.go
type RVBucket (line 17) | type RVBucket struct
type DefaultRouteViewCounter (line 25) | type DefaultRouteViewCounter struct
method Tick (line 57) | func (co *DefaultRouteViewCounter) Tick() (err error) {
method insertChunk (line 99) | func (co *DefaultRouteViewCounter) insertChunk(count int64, avg, route...
method insert5Chunk (line 106) | func (co *DefaultRouteViewCounter) insert5Chunk(rvs []RVCount) error {
method Bump (line 126) | func (co *DefaultRouteViewCounter) Bump(route int) {
method Bump2 (line 140) | func (co *DefaultRouteViewCounter) Bump2(route int, t time.Time) {
method Bump3 (line 165) | func (co *DefaultRouteViewCounter) Bump3(route int, nano int64) {
function NewDefaultRouteViewCounter (line 31) | func NewDefaultRouteViewCounter(acc *qgen.Accumulator) (*DefaultRouteVie...
type RVCount (line 51) | type RVCount struct
FILE: common/counters/systems.go
type DefaultOSViewCounter (line 14) | type DefaultOSViewCounter struct
method Tick (line 30) | func (co *DefaultOSViewCounter) Tick() error {
method insertChunk (line 40) | func (co *DefaultOSViewCounter) insertChunk(count int64, os int) error {
method Bump (line 50) | func (co *DefaultOSViewCounter) Bump(id int) {
function NewDefaultOSViewCounter (line 19) | func NewDefaultOSViewCounter(acc *qgen.Accumulator) (*DefaultOSViewCount...
FILE: common/counters/topics.go
type DefaultTopicCounter (line 14) | type DefaultTopicCounter struct
method Tick (line 33) | func (co *DefaultTopicCounter) Tick() (e error) {
method Bump (line 52) | func (co *DefaultTopicCounter) Bump() {
method insertChunk (line 56) | func (co *DefaultTopicCounter) insertChunk(count int64) error {
function NewTopicCounter (line 21) | func NewTopicCounter() (*DefaultTopicCounter, error) {
FILE: common/counters/topics_views.go
type DefaultTopicViewCounter (line 19) | type DefaultTopicViewCounter struct
method handleInsertListBuf (line 77) | func (co *DefaultTopicViewCounter) handleInsertListBuf(i, i2 int) error {
method Tick (line 97) | func (co *DefaultTopicViewCounter) Tick() error {
method WeekResetInit (line 142) | func (co *DefaultTopicViewCounter) WeekResetInit() error {
method WeekResetTick (line 169) | func (co *DefaultTopicViewCounter) WeekResetTick() (e error) {
method insertChunk (line 188) | func (co *DefaultTopicViewCounter) insertChunk(count, topicID int) (er...
method Bump (line 228) | func (co *DefaultTopicViewCounter) Bump(topicID int) {
function NewDefaultTopicViewCounter (line 36) | func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
type TopicViewInsert (line 67) | type TopicViewInsert struct
type SavedTick (line 72) | type SavedTick struct
FILE: common/disk.go
function DirSize (line 8) | func DirSize(path string) (int, error) {
FILE: common/email.go
function SendActivationEmail (line 13) | func SendActivationEmail(username, email, token string) error {
function SendValidationEmail (line 24) | func SendValidationEmail(username, email, token string) error {
function SendEmail (line 47) | func SendEmail(email, subject, msg string) (err error) {
FILE: common/email_store.go
type Email (line 11) | type Email struct
type EmailStore (line 19) | type EmailStore interface
type DefaultEmailStore (line 28) | type DefaultEmailStore struct
method Get (line 49) | func (s *DefaultEmailStore) Get(user *User, email string) (Email, erro...
method GetEmailsByUser (line 55) | func (s *DefaultEmailStore) GetEmailsByUser(user *User) (emails []Emai...
method Add (line 75) | func (s *DefaultEmailStore) Add(uid int, email, token string) error {
method Delete (line 81) | func (s *DefaultEmailStore) Delete(uid int, email string) error {
method VerifyEmail (line 86) | func (s *DefaultEmailStore) VerifyEmail(email string) error {
function NewDefaultEmailStore (line 36) | func NewDefaultEmailStore(acc *qgen.Accumulator) (*DefaultEmailStore, er...
FILE: common/errors.go
type ErrorItem (line 15) | type ErrorItem struct
type RouteError (line 34) | type RouteError interface
type RouteErrorImpl (line 44) | type RouteErrorImpl struct
method Type (line 52) | func (err *RouteErrorImpl) Type() string {
method Error (line 60) | func (err *RouteErrorImpl) Error() string {
method Cause (line 64) | func (err *RouteErrorImpl) Cause() string {
method JSON (line 72) | func (err *RouteErrorImpl) JSON() bool {
method Handled (line 77) | func (err *RouteErrorImpl) Handled() bool {
method Wrap (line 82) | func (err *RouteErrorImpl) Wrap(userErr string) {
function HandledRouteError (line 87) | func HandledRouteError() RouteError {
function Error (line 91) | func Error(errmsg string) RouteError {
function FromError (line 95) | func FromError(err error) RouteError {
function ErrorJSQ (line 99) | func ErrorJSQ(errmsg string, js bool) RouteError {
function SysError (line 103) | func SysError(errmsg string) RouteError {
function LogError (line 109) | func LogError(err error, extra ...string) {
function LogWarning (line 114) | func LogWarning(err error, extra ...string) {
function errorHeader (line 136) | func errorHeader(w http.ResponseWriter, u *User, title string) *Header {
function InternalError (line 147) | func InternalError(err error, w http.ResponseWriter, r *http.Request) Ro...
function InternalErrorJSQ (line 156) | func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request,...
function InternalErrorJS (line 165) | func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) ...
function DatabaseError (line 173) | func DatabaseError(w http.ResponseWriter, r *http.Request) RouteError {
function InternalErrorXML (line 179) | func InternalErrorXML(err error, w http.ResponseWriter, r *http.Request)...
function SilentInternalErrorXML (line 189) | func SilentInternalErrorXML(err error, w http.ResponseWriter, r *http.Re...
function PreError (line 199) | func PreError(errmsg string, w http.ResponseWriter, r *http.Request) Rou...
function PreErrorJS (line 205) | func PreErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) R...
function PreErrorJSQ (line 211) | func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, ...
function LocalError (line 227) | func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, u...
function LocalErrorf (line 231) | func LocalErrorf(errmsg string, w http.ResponseWriter, r *http.Request, ...
function SimpleError (line 235) | func SimpleError(errmsg string, w http.ResponseWriter, r *http.Request, ...
function LocalErrorJSQ (line 246) | func LocalErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request...
function LocalErrorJS (line 253) | func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request)...
function NoPermissions (line 261) | func NoPermissions(w http.ResponseWriter, r *http.Request, u *User) Rout...
function NoPermissionsJSQ (line 267) | func NoPermissionsJSQ(w http.ResponseWriter, r *http.Request, u *User, j...
function NoPermissionsJS (line 274) | func NoPermissionsJS(w http.ResponseWriter, r *http.Request, u *User) Ro...
function Banned (line 281) | func Banned(w http.ResponseWriter, r *http.Request, u *User) RouteError {
function BannedJSQ (line 289) | func BannedJSQ(w http.ResponseWriter, r *http.Request, user *User, js bo...
function BannedJS (line 296) | func BannedJS(w http.ResponseWriter, r *http.Request, u *User) RouteError {
function LoginRequiredJSQ (line 303) | func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, u *User, j...
function LoginRequired (line 312) | func LoginRequired(w http.ResponseWriter, r *http.Request, u *User) Rout...
function LoginRequiredJS (line 317) | func LoginRequiredJS(w http.ResponseWriter, r *http.Request, u *User) Ro...
function SecurityError (line 325) | func SecurityError(w http.ResponseWriter, r *http.Request, u *User) Rout...
function MicroNotFound (line 337) | func MicroNotFound(w http.ResponseWriter, r *http.Request) RouteError {
function NotFound (line 347) | func NotFound(w http.ResponseWriter, r *http.Request, h *Header) RouteEr...
function NotFoundJS (line 352) | func NotFoundJS(w http.ResponseWriter, r *http.Request) RouteError {
function NotFoundJSQ (line 358) | func NotFoundJSQ(w http.ResponseWriter, r *http.Request, h *Header, js b...
function CustomError (line 369) | func CustomError(errmsg string, errcode int, errtitle string, w http.Res...
function CustomErrorJSQ (line 384) | func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http....
function CustomErrorJS (line 392) | func CustomErrorJS(errmsg string, errcode int, w http.ResponseWriter, r ...
function writeJsonError (line 399) | func writeJsonError(errmsg string, w http.ResponseWriter) {
function handleErrorTemplate (line 403) | func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi Erro...
FILE: common/extend.go
type PluginList (line 23) | type PluginList
method Add (line 28) | func (l PluginList) Add(pl *Plugin) {
method Load (line 444) | func (l PluginList) Load() error {
function buildPlugin (line 33) | func buildPlugin(pl *Plugin) {
type HookTable (line 51) | type HookTable struct
method HookNoRet (line 166) | func (t *HookTable) HookNoRet(name string, data interface{}) {
method HookSkip (line 173) | func (t *HookTable) HookSkip(name string, data interface{}) (skip bool) {
method Vhook (line 184) | func (t *HookTable) Vhook(name string, data ...interface{}) interface{} {
method VhookNoRet (line 191) | func (t *HookTable) VhookNoRet(name string, data ...interface{}) {
method VhookNeedHook (line 198) | func (t *HookTable) VhookNeedHook(name string, data ...interface{}) (r...
method VhookSkippable (line 206) | func (t *HookTable) VhookSkippable(name string, data ...interface{}) (...
method Sshook (line 230) | func (t *HookTable) Sshook(name, data string) string {
function init (line 64) | func init() {
function RebuildHookTable (line 142) | func RebuildHookTable() {
function unsafeRebuildHookTable (line 148) | func unsafeRebuildHookTable() {
function GetHookTable (line 154) | func GetHookTable() *HookTable {
type Message (line 251) | type Message interface
type PageInt (line 259) | type PageInt interface
type Plugin (line 334) | type Plugin struct
method BypassActive (line 362) | func (pl *Plugin) BypassActive() (active bool, err error) {
method InDatabase (line 370) | func (pl *Plugin) InDatabase() (exists bool, err error) {
method SetActive (line 380) | func (pl *Plugin) SetActive(active bool) (err error) {
method SetInstalled (line 389) | func (pl *Plugin) SetInstalled(installed bool) (err error) {
method AddToDatabase (line 400) | func (pl *Plugin) AddToDatabase(active, installed bool) (err error) {
method AddHook (line 473) | func (pl *Plugin) AddHook(name string, hInt interface{}) {
method RemoveHook (line 529) | func (pl *Plugin) RemoveHook(name string, hInt interface{}) {
type PluginMetaData (line 357) | type PluginMetaData struct
type ExtendStmts (line 409) | type ExtendStmts struct
function init (line 420) | func init() {
function InitExtend (line 435) | func InitExtend() error {
function InitPlugins (line 603) | func InitPlugins() {
function RunTaskHook (line 621) | func RunTaskHook(name string) error {
function RunPreRenderHook (line 630) | func RunPreRenderHook(name string, w http.ResponseWriter, r *http.Reques...
FILE: common/files.go
type SFileList (line 34) | type SFileList struct
method JSTmplInit (line 67) | func (l SFileList) JSTmplInit() error {
method Init (line 322) | func (l SFileList) Init() error {
method Add (line 386) | func (l SFileList) Add(path, prefix string) error {
method Get (line 444) | func (l SFileList) Get(path string) (file *SFile, exists bool) {
method GetShort (line 452) | func (l SFileList) GetShort(name string) (file *SFile, exists bool) {
method Set (line 459) | func (l SFileList) Set(name string, data *SFile) {
type SFile (line 40) | type SFile struct
type CSSData (line 63) | type CSSData struct
function CompressBytesGzip (line 473) | func CompressBytesGzip(in []byte) (b []byte, err error) {
function CompressBytesBrotli (line 498) | func CompressBytesBrotli(in []byte) ([]byte, error) {
FILE: common/forum.go
type ForumAdmin (line 15) | type ForumAdmin struct
type Forum (line 25) | type Forum struct
method Copy (line 72) | func (f *Forum) Copy() (fcopy Forum) {
method Update (line 78) | func (f *Forum) Update(name, desc string, active bool, preset string) ...
method SetPreset (line 98) | func (f *Forum) SetPreset(preset string, gid int) error {
method SetPerms (line 107) | func (f *Forum) SetPerms(fperms *ForumPerms, preset string, gid int) (...
type ForumSimple (line 47) | type ForumSimple struct
type ForumStmts (line 54) | type ForumStmts struct
function init (line 61) | func init() {
type SortForum (line 133) | type SortForum
method Len (line 135) | func (sf SortForum) Len() int {
method Swap (line 138) | func (sf SortForum) Swap(i, j int) {
method Less (line 151) | func (sf SortForum) Less(i, j int) bool {
function BlankForum (line 161) | func BlankForum(fid int, link, name, desc string, active bool, preset st...
function BuildForumURL (line 165) | func BuildForumURL(slug string, fid int) string {
function GetForumURLPrefix (line 172) | func GetForumURLPrefix() string {
FILE: common/forum_actions.go
constant ForumActionDelete (line 16) | ForumActionDelete = iota
constant ForumActionLock (line 17) | ForumActionLock
constant ForumActionUnlock (line 18) | ForumActionUnlock
constant ForumActionMove (line 19) | ForumActionMove
function ConvStringToAct (line 22) | func ConvStringToAct(s string) int {
function ConvActToString (line 35) | func ConvActToString(a int) string {
type ForumActionStmts (line 51) | type ForumActionStmts struct
type ForumAction (line 60) | type ForumAction struct
method Run (line 86) | func (a *ForumAction) Run() error {
method runQ (line 100) | func (a *ForumAction) runQ(stmt *sql.Stmt, days int, f func(t *Topic) ...
method runDaysAfterTopicCreation (line 119) | func (a *ForumAction) runDaysAfterTopicCreation() (e error) {
method runDaysAfterTopicLastReply (line 154) | func (a *ForumAction) runDaysAfterTopicLastReply() (e error) {
method TopicCreation (line 182) | func (a *ForumAction) TopicCreation(tid int) error {
function init (line 70) | func init() {
type ForumActionStoreInt (line 189) | type ForumActionStoreInt interface
type DefaultForumActionStore (line 204) | type DefaultForumActionStore struct
method DailyTick (line 234) | func (s *DefaultForumActionStore) DailyTick() error {
method Get (line 247) | func (s *DefaultForumActionStore) Get(id int) (*ForumAction, error) {
method GetInForum (line 255) | func (s *DefaultForumActionStore) GetInForum(fid int) (fas []*ForumAct...
method GetAll (line 273) | func (s *DefaultForumActionStore) GetAll() (fas []*ForumAction, e erro...
method GetNewTopicActions (line 291) | func (s *DefaultForumActionStore) GetNewTopicActions(fid int) (fas []*...
method Add (line 309) | func (s *DefaultForumActionStore) Add(fa *ForumAction) (int, error) {
method Delete (line 318) | func (s *DefaultForumActionStore) Delete(id int) error {
method Exists (line 323) | func (s *DefaultForumActionStore) Exists(id int) bool {
method Count (line 331) | func (s *DefaultForumActionStore) Count() (count int) {
method CountInForum (line 339) | func (s *DefaultForumActionStore) CountInForum(fid int) (count int) {
function NewDefaultForumActionStore (line 217) | func NewDefaultForumActionStore(acc *qgen.Accumulator) (*DefaultForumAct...
FILE: common/forum_perms.go
type ForumPerms (line 28) | type ForumPerms struct
function PresetToPermmap (line 49) | func PresetToPermmap(preset string) (out map[string]*ForumPerms) {
function PermmapToQuery (line 91) | func PermmapToQuery(permmap map[string]*ForumPerms, fid int) error {
function ReplaceForumPermsForGroup (line 180) | func ReplaceForumPermsForGroup(gid int, presetSet map[int]string, permSe...
function ReplaceForumPermsForGroupTx (line 195) | func ReplaceForumPermsForGroupTx(tx *sql.Tx, gid int, presetSets map[int...
function ForumPermsToGroupForumPreset (line 225) | func ForumPermsToGroupForumPreset(fp *ForumPerms) string {
function GroupForumPresetToForumPerms (line 253) | func GroupForumPresetToForumPerms(preset string) (fperms *ForumPerms, ch...
function BlankForumPerms (line 269) | func BlankForumPerms() *ForumPerms {
function ReadWriteForumPerms (line 273) | func ReadWriteForumPerms() *ForumPerms {
function ReadReplyForumPerms (line 284) | func ReadReplyForumPerms() *ForumPerms {
function ReadForumPerms (line 294) | func ReadForumPerms() *ForumPerms {
function AllForumPerms (line 303) | func AllForumPerms() *ForumPerms {
FILE: common/forum_perms_store.go
type ForumPermsStore (line 13) | type ForumPermsStore interface
type ForumPermsCache (line 22) | type ForumPermsCache interface
type MemoryForumPermsStore (line 25) | type MemoryForumPermsStore struct
method Init (line 47) | func (s *MemoryForumPermsStore) Init() error {
method ReloadAll (line 53) | func (s *MemoryForumPermsStore) ReloadAll() error {
method parseForumPerm (line 71) | func (s *MemoryForumPermsStore) parseForumPerm(perms []byte) (pperms *...
method Reload (line 80) | func (s *MemoryForumPermsStore) Reload(fid int) error {
method reload (line 93) | func (s *MemoryForumPermsStore) reload(fid int) error {
method recalcCanSeeAll (line 133) | func (s *MemoryForumPermsStore) recalcCanSeeAll() error {
method GetAllMap (line 205) | func (s *MemoryForumPermsStore) GetAllMap() (bigMap map[int]map[int]*F...
method Get (line 224) | func (s *MemoryForumPermsStore) Get(fid, gid int) (fp *ForumPerms, err...
method GetCopy (line 249) | func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, ...
function NewMemoryForumPermsStore (line 35) | func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
FILE: common/forum_store.go
type ForumStore (line 29) | type ForumStore interface
type ForumCache (line 56) | type ForumCache interface
type MemoryForumStore (line 64) | type MemoryForumStore struct
method LoadForums (line 104) | func (s *MemoryForumStore) LoadForums() error {
method rebuildView (line 148) | func (s *MemoryForumStore) rebuildView() {
method Each (line 163) | func (s *MemoryForumStore) Each(h func(*Forum) error) (err error) {
method DirtyGet (line 174) | func (s *MemoryForumStore) DirtyGet(id int) *Forum {
method CacheGet (line 182) | func (s *MemoryForumStore) CacheGet(id int) (*Forum, error) {
method Get (line 190) | func (s *MemoryForumStore) Get(id int) (*Forum, error) {
method BypassGet (line 208) | func (s *MemoryForumStore) BypassGet(id int) (*Forum, error) {
method BulkGetCopy (line 229) | func (s *MemoryForumStore) BulkGetCopy(ids []int) (forums []Forum, err...
method Reload (line 241) | func (s *MemoryForumStore) Reload(id int) error {
method CacheSet (line 250) | func (s *MemoryForumStore) CacheSet(f *Forum) error {
method GetAll (line 257) | func (s *MemoryForumStore) GetAll() (forumView []*Forum, err error) {
method GetAllIDs (line 267) | func (s *MemoryForumStore) GetAllIDs() (ids []int, err error) {
method GetAllVisible (line 276) | func (s *MemoryForumStore) GetAllVisible() (forumView []*Forum, err er...
method GetAllVisibleIDs (line 281) | func (s *MemoryForumStore) GetAllVisibleIDs() ([]int, error) {
method Exists (line 299) | func (s *MemoryForumStore) Exists(id int) bool {
method CacheDelete (line 308) | func (s *MemoryForumStore) CacheDelete(id int) {
method Delete (line 314) | func (s *MemoryForumStore) Delete(id int) error {
method AddTopic (line 323) | func (s *MemoryForumStore) AddTopic(tid, uid, fid int) error {
method RefreshTopic (line 336) | func (s *MemoryForumStore) RefreshTopic(fid int) (err error) {
method RemoveTopic (line 368) | func (s *MemoryForumStore) RemoveTopic(fid int) error {
method RemoveTopics (line 375) | func (s *MemoryForumStore) RemoveTopics(fid, count int) error {
method UpdateLastTopic (line 385) | func (s *MemoryForumStore) UpdateLastTopic(tid, uid, fid int) error {
method Create (line 394) | func (s *MemoryForumStore) Create(name, desc string, active bool, pres...
method UpdateOrder (line 422) | func (s *MemoryForumStore) UpdateOrder(updateMap map[int]int) error {
method Length (line 434) | func (s *MemoryForumStore) Length() (len int) {
method Count (line 444) | func (s *MemoryForumStore) Count() (count int) {
function NewMemoryForumStore (line 81) | func NewMemoryForumStore() (*MemoryForumStore, error) {
FILE: common/gauth/authenticator.go
function prefix0 (line 18) | func prefix0(otp string) string {
function GetHOTPToken (line 28) | func GetHOTPToken(secret string, interval int64) (string, error) {
function GetTOTPToken (line 63) | func GetTOTPToken(secret string) (string, error) {
FILE: common/group.go
type GroupAdmin (line 12) | type GroupAdmin struct
type Group (line 22) | type Group struct
method ChangeRank (line 59) | func (g *Group) ChangeRank(isAdmin, isMod, isBanned bool) (err error) {
method Update (line 68) | func (g *Group) Update(name, tag string) (err error) {
method UpdatePerms (line 78) | func (g *Group) UpdatePerms(perms map[string]bool) (err error) {
method Copy (line 91) | func (g *Group) Copy() Group {
method CopyPtr (line 95) | func (g *Group) CopyPtr() (co *Group) {
type GroupStmts (line 37) | type GroupStmts struct
function init (line 45) | func init() {
type SortGroup (line 103) | type SortGroup
method Len (line 105) | func (sg SortGroup) Len() int {
method Swap (line 108) | func (sg SortGroup) Swap(i, j int) {
method Less (line 111) | func (sg SortGroup) Less(i, j int) bool {
FILE: common/group_store.go
type GroupStore (line 19) | type GroupStore interface
type GroupCache (line 32) | type GroupCache interface
type MemoryGroupStore (line 39) | type MemoryGroupStore struct
method LoadGroups (line 64) | func (s *MemoryGroupStore) LoadGroups() error {
method dirtyGetUnsafe (line 101) | func (s *MemoryGroupStore) dirtyGetUnsafe(id int) *Group {
method DirtyGet (line 110) | func (s *MemoryGroupStore) DirtyGet(id int) *Group {
method Get (line 121) | func (s *MemoryGroupStore) Get(id int) (*Group, error) {
method GetCopy (line 132) | func (s *MemoryGroupStore) GetCopy(id int) (Group, error) {
method Reload (line 142) | func (s *MemoryGroupStore) Reload(id int) error {
method initGroup (line 166) | func (s *MemoryGroupStore) initGroup(g *Group) error {
method SetCanSee (line 196) | func (s *MemoryGroupStore) SetCanSee(gid int, canSee []int) error {
method CacheSet (line 211) | func (s *MemoryGroupStore) CacheSet(g *Group) error {
method Exists (line 219) | func (s *MemoryGroupStore) Exists(id int) bool {
method Create (line 228) | func (s *MemoryGroupStore) Create(name, tag string, isAdmin, isMod, is...
method CacheAdd (line 306) | func (s *MemoryGroupStore) CacheAdd(g *Group) error {
method GetAll (line 314) | func (s *MemoryGroupStore) GetAll() (results []*Group, err error) {
method GetAllMap (line 327) | func (s *MemoryGroupStore) GetAllMap() (map[int]*Group, error) {
method GetRange (line 335) | func (s *MemoryGroupStore) GetRange(lower, higher int) (groups []*Grou...
method Length (line 363) | func (s *MemoryGroupStore) Length() int {
method Count (line 369) | func (s *MemoryGroupStore) Count() (count int) {
function NewMemoryGroupStore (line 50) | func NewMemoryGroupStore() (*MemoryGroupStore, error) {
FILE: common/ip_search.go
type IPSearcher (line 11) | type IPSearcher interface
type DefaultIPSearcher (line 15) | type DefaultIPSearcher struct
method Lookup (line 37) | func (s *DefaultIPSearcher) Lookup(ip string) (uids []int, e error) {
function NewDefaultIPSearcher (line 23) | func NewDefaultIPSearcher() (*DefaultIPSearcher, error) {
FILE: common/likes.go
type LikeStore (line 11) | type LikeStore interface
type DefaultLikeStore (line 18) | type DefaultLikeStore struct
method BulkExists (line 33) | func (s *DefaultLikeStore) BulkExists(ids []int, sentBy int, targetTyp...
method BulkExistsFunc (line 61) | func (s *DefaultLikeStore) BulkExistsFunc(ids []int, sentBy int, targe...
method Delete (line 90) | func (s *DefaultLikeStore) Delete(targetID int, targetType string) err...
method Count (line 97) | func (s *DefaultLikeStore) Count() (count int) {
function NewDefaultLikeStore (line 24) | func NewDefaultLikeStore(acc *qgen.Accumulator) (*DefaultLikeStore, erro...
FILE: common/menu_item_store.go
type DefaultMenuItemStore (line 5) | type DefaultMenuItemStore struct
method Add (line 16) | func (s *DefaultMenuItemStore) Add(i MenuItem) {
method Get (line 22) | func (s *DefaultMenuItemStore) Get(id int) (MenuItem, error) {
function NewDefaultMenuItemStore (line 10) | func NewDefaultMenuItemStore() *DefaultMenuItemStore {
FILE: common/menu_store.go
type DefaultMenuStore (line 13) | type DefaultMenuStore struct
method GetAllMap (line 26) | func (s *DefaultMenuStore) GetAllMap() (out map[int]*MenuListHolder) {
method Get (line 34) | func (s *DefaultMenuStore) Get(mid int) (*MenuListHolder, error) {
method Items (line 42) | func (s *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err err...
method Load (line 56) | func (s *DefaultMenuStore) Load(mid int) error {
method ItemStore (line 73) | func (s *DefaultMenuStore) ItemStore() *DefaultMenuItemStore {
function NewDefaultMenuStore (line 18) | func NewDefaultMenuStore() *DefaultMenuStore {
FILE: common/menus.go
type MenuItemList (line 17) | type MenuItemList
type MenuListHolder (line 19) | type MenuListHolder struct
method LoadTmpl (line 102) | func (h *MenuListHolder) LoadTmpl(name string) (t MenuTmpl, e error) {
method UpdateOrder (line 111) | func (h *MenuListHolder) UpdateOrder(updateMap map[int]int) error {
method LoadTmpls (line 122) | func (h *MenuListHolder) LoadTmpls() (tmpls map[string]MenuTmpl, e err...
method Preparse (line 141) | func (h *MenuListHolder) Preparse() error {
method Parse (line 269) | func (h *MenuListHolder) Parse(name string, tmplData []byte) (menuTmpl...
method Scan (line 330) | func (h *MenuListHolder) Scan(tmpls map[string]MenuTmpl, showItem func...
method ScanItem (line 345) | func (h *MenuListHolder) ScanItem(tmpls map[string]MenuTmpl, mitem Men...
method Build (line 421) | func (h *MenuListHolder) Build(w io.Writer, user *User, pathPrefix str...
type menuPath (line 25) | type menuPath struct
type menuTmpl (line 30) | type menuTmpl struct
type MenuItem (line 36) | type MenuItem struct
method Commit (line 79) | func (i MenuItem) Commit() error {
method Create (line 85) | func (i MenuItem) Create() (int, error) {
method Delete (line 96) | func (i MenuItem) Delete() error {
type MenuItemStmts (line 57) | type MenuItemStmts struct
function init (line 66) | func init() {
function nextCharIs (line 171) | func nextCharIs(tmplData []byte, i int, expects byte) bool {
function peekNextChar (line 178) | func peekNextChar(tmplData []byte, i int) byte {
function skipUntilIfExists (line 185) | func skipUntilIfExists(tmplData []byte, i int, expects byte) (newI int, ...
function skipUntilIfExistsOrLine (line 195) | func skipUntilIfExistsOrLine(tmplData []byte, i int, expects byte) (newI...
function skipUntilCharsExist (line 207) | func skipUntilCharsExist(tmplData []byte, i int, expects []byte) (newI i...
function skipAllUntilCharsExist (line 221) | func skipAllUntilCharsExist(tmplData []byte, i int, expects []byte) (new...
type menuRenderItem (line 247) | type menuRenderItem struct
type MenuTmpl (line 252) | type MenuTmpl struct
function menuDumpSlice (line 259) | func menuDumpSlice(outerSlice [][]byte) {
FILE: common/meta/meta_store.go
type MetaStore (line 10) | type MetaStore interface
type DefaultMetaStore (line 17) | type DefaultMetaStore struct
method Get (line 33) | func (s *DefaultMetaStore) Get(name string) (val string, e error) {
method setVal (line 39) | func (s *DefaultMetaStore) setVal(name string, val interface{}) error {
method Set (line 51) | func (s *DefaultMetaStore) Set(name, val string) error {
method SetInt (line 55) | func (s *DefaultMetaStore) SetInt(name string, val int) error {
method SetInt64 (line 59) | func (s *DefaultMetaStore) SetInt64(name string, val int64) error {
function NewDefaultMetaStore (line 23) | func NewDefaultMetaStore(acc *qgen.Accumulator) (*DefaultMetaStore, erro...
FILE: common/mfa_store.go
type MFAItemStmts (line 14) | type MFAItemStmts struct
function init (line 21) | func init() {
type MFAItem (line 31) | type MFAItem struct
method BurnScratch (line 37) | func (i *MFAItem) BurnScratch(index int) error {
method Delete (line 51) | func (i *MFAItem) Delete() error {
function mfaCreateScratch (line 56) | func mfaCreateScratch() (string, error) {
type MFAStore (line 61) | type MFAStore interface
type SQLMFAStore (line 66) | type SQLMFAStore struct
method Get (line 79) | func (s *SQLMFAStore) Get(id int) (*MFAItem, error) {
method Create (line 87) | func (s *SQLMFAStore) Create(secret string, uid int) (err error) {
function NewSQLMFAStore (line 71) | func NewSQLMFAStore(acc *qgen.Accumulator) (*SQLMFAStore, error) {
FILE: common/misc_logs.go
type RegLogItem (line 13) | type RegLogItem struct
method Commit (line 44) | func (l *RegLogItem) Commit() error {
method Create (line 49) | func (l *RegLogItem) Create() (id int, e error) {
type RegLogStmts (line 23) | type RegLogStmts struct
function init (line 30) | func init() {
type RegLogStore (line 55) | type RegLogStore interface
type SQLRegLogStore (line 63) | type SQLRegLogStore struct
method Count (line 82) | func (s *SQLRegLogStore) Count() (count int) {
method GetOffset (line 86) | func (s *SQLRegLogStore) GetOffset(offset, perPage int) (logs []RegLog...
method DeleteOlderThanDays (line 106) | func (s *SQLRegLogStore) DeleteOlderThanDays(days int) error {
method Purge (line 112) | func (s *SQLRegLogStore) Purge() error {
function NewRegLogStore (line 71) | func NewRegLogStore(acc *qgen.Accumulator) (*SQLRegLogStore, error) {
type LoginLogItem (line 117) | type LoginLogItem struct
method Commit (line 145) | func (l *LoginLogItem) Commit() error {
method Create (line 150) | func (l *LoginLogItem) Create() (id int, e error) {
type LoginLogStmts (line 125) | type LoginLogStmts struct
function init (line 132) | func init() {
type LoginLogStore (line 160) | type LoginLogStore interface
type SQLLoginLogStore (line 169) | type SQLLoginLogStore struct
method Count (line 190) | func (s *SQLLoginLogStore) Count() (count int) {
method CountUser (line 194) | func (s *SQLLoginLogStore) CountUser(uid int) (count int) {
method GetOffset (line 198) | func (s *SQLLoginLogStore) GetOffset(uid, offset, perPage int) (logs [...
method DeleteOlderThanDays (line 218) | func (s *SQLLoginLogStore) DeleteOlderThanDays(days int) error {
method Purge (line 224) | func (s *SQLLoginLogStore) Purge() error {
function NewLoginLogStore (line 178) | func NewLoginLogStore(acc *qgen.Accumulator) (*SQLLoginLogStore, error) {
FILE: common/module_ottojs.go
type OttoPluginLang (line 15) | type OttoPluginLang struct
method Init (line 28) | func (js *OttoPluginLang) Init() (err error) {
method GetName (line 34) | func (js *OttoPluginLang) GetName() string {
method GetExts (line 38) | func (js *OttoPluginLang) GetExts() []string {
method AddPlugin (line 42) | func (js *OttoPluginLang) AddPlugin(meta PluginMeta) (plugin *Plugin, ...
function init (line 21) | func init() {
FILE: common/no_websockets.go
type WSHub (line 14) | type WSHub struct
method guestCount (line 16) | func (_ *WSHub) guestCount() int { return 0 }
method userCount (line 18) | func (_ *WSHub) userCount() int { return 0 }
method broadcastMessage (line 20) | func (hub *WSHub) broadcastMessage(_ string) error { return nil }
method pushMessage (line 22) | func (hub *WSHub) pushMessage(_ int, _ string) error {
method pushAlert (line 26) | func (hub *WSHub) pushAlert(_ int, _ int, _ string, _ string, _ int, _...
method pushAlerts (line 30) | func (hub *WSHub) pushAlerts(_ []int, _ int, _ string, _ string, _ int...
function RouteWebsockets (line 34) | func RouteWebsockets(_ http.ResponseWriter, _ *http.Request, _ User) {}
FILE: common/null_reply_cache.go
type NullReplyCache (line 4) | type NullReplyCache struct
method Get (line 13) | func (c *NullReplyCache) Get(id int) (*Reply, error) {
method GetUnsafe (line 16) | func (c *NullReplyCache) GetUnsafe(id int) (*Reply, error) {
method BulkGet (line 19) | func (c *NullReplyCache) BulkGet(ids []int) (list []*Reply) {
method Set (line 22) | func (c *NullReplyCache) Set(_ *Reply) error {
method Add (line 25) | func (c *NullReplyCache) Add(_ *Reply) error {
method AddUnsafe (line 28) | func (c *NullReplyCache) AddUnsafe(_ *Reply) error {
method Remove (line 31) | func (c *NullReplyCache) Remove(id int) error {
method RemoveUnsafe (line 34) | func (c *NullReplyCache) RemoveUnsafe(id int) error {
method Flush (line 37) | func (c *NullReplyCache) Flush() {
method Length (line 39) | func (c *NullReplyCache) Length() int {
method SetCapacity (line 42) | func (c *NullReplyCache) SetCapacity(_ int) {
method GetCapacity (line 44) | func (c *NullReplyCache) GetCapacity() int {
function NewNullReplyCache (line 8) | func NewNullReplyCache() *NullReplyCache {
FILE: common/null_topic_cache.go
type NullTopicCache (line 4) | type NullTopicCache struct
method Get (line 13) | func (c *NullTopicCache) Get(id int) (*Topic, error) {
method GetUnsafe (line 16) | func (c *NullTopicCache) GetUnsafe(id int) (*Topic, error) {
method BulkGet (line 19) | func (c *NullTopicCache) BulkGet(ids []int) (list []*Topic) {
method Set (line 22) | func (c *NullTopicCache) Set(_ *Topic) error {
method Add (line 25) | func (c *NullTopicCache) Add(_ *Topic) error {
method AddUnsafe (line 28) | func (c *NullTopicCache) AddUnsafe(_ *Topic) error {
method Remove (line 31) | func (c *NullTopicCache) Remove(id int) error {
method RemoveMany (line 34) | func (c *NullTopicCache) RemoveMany(ids []int) error {
method RemoveUnsafe (line 37) | func (c *NullTopicCache) RemoveUnsafe(id int) error {
method Flush (line 40) | func (c *NullTopicCache) Flush() {
method Length (line 42) | func (c *NullTopicCache) Length() int {
method SetCapacity (line 45) | func (c *NullTopicCache) SetCapacity(_ int) {
method GetCapacity (line 47) | func (c *NullTopicCache) GetCapacity() int {
function NewNullTopicCache (line 8) | func NewNullTopicCache() *NullTopicCache {
FILE: common/null_user_cache.go
type NullUserCache (line 4) | type NullUserCache struct
method DeallocOverflow (line 13) | func (c *NullUserCache) DeallocOverflow(evictPriority bool) (evicted i...
method Get (line 16) | func (c *NullUserCache) Get(id int) (*User, error) {
method Getn (line 20) | func (c *NullUserCache) Getn(id int) *User {
method BulkGet (line 23) | func (c *NullUserCache) BulkGet(ids []int) (list []*User) {
method GetUnsafe (line 26) | func (c *NullUserCache) GetUnsafe(id int) (*User, error) {
method Set (line 29) | func (c *NullUserCache) Set(_ *User) error {
method Add (line 32) | func (c *NullUserCache) Add(_ *User) error {
method AddUnsafe (line 35) | func (c *NullUserCache) AddUnsafe(_ *User) error {
method Remove (line 38) | func (c *NullUserCache) Remove(id int) error {
method RemoveUnsafe (line 41) | func (c *NullUserCache) RemoveUnsafe(id int) error {
method BulkRemove (line 44) | func (c *NullUserCache) BulkRemove(ids []int) {}
method Flush (line 45) | func (c *NullUserCache) Flush() {
method Length (line 47) | func (c *NullUserCache) Length() int {
method SetCapacity (line 50) | func (c *NullUserCache) SetCapacity(_ int) {
method GetCapacity (line 52) | func (c *NullUserCache) GetCapacity() int {
function NewNullUserCache (line 8) | func NewNullUserCache() *NullUserCache {
FILE: common/page_store.go
type CustomPageStmts (line 11) | type CustomPageStmts struct
function init (line 18) | func init() {
type CustomPage (line 28) | type CustomPage struct
method AddAllowedGroup (line 41) | func (p *CustomPage) AddAllowedGroup(gid int) {
method getRawAllowedGroups (line 45) | func (p *CustomPage) getRawAllowedGroups() (rawAllowedGroups string) {
method Commit (line 55) | func (p *CustomPage) Commit() error {
method Create (line 61) | func (p *CustomPage) Create() (int, error) {
function BlankCustomPage (line 37) | func BlankCustomPage() *CustomPage {
type PageStore (line 73) | type PageStore interface
type DefaultPageStore (line 83) | type DefaultPageStore struct
method Count (line 103) | func (s *DefaultPageStore) Count() (count int) {
method parseAllowedGroups (line 111) | func (s *DefaultPageStore) parseAllowedGroups(raw string, page *Custom...
method Get (line 125) | func (s *DefaultPageStore) Get(id int) (*CustomPage, error) {
method GetByName (line 135) | func (s *DefaultPageStore) GetByName(name string) (*CustomPage, error) {
method GetOffset (line 145) | func (s *DefaultPageStore) GetOffset(offset, perPage int) (pages []*Cu...
method Reload (line 169) | func (s *DefaultPageStore) Reload(id int) error {
method Delete (line 173) | func (s *DefaultPageStore) Delete(id int) error {
function NewDefaultPageStore (line 91) | func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, erro...
FILE: common/pages.go
type Header (line 20) | type Header struct
method getScript (line 61) | func (h *Header) getScript(name string) HScript {
method AddScript (line 72) | func (h *Header) AddScript(name string) {
method AddPreScriptAsync (line 77) | func (h *Header) AddPreScriptAsync(name string) {
method AddScriptAsync (line 81) | func (h *Header) AddScriptAsync(name string) {
method AddSheet (line 89) | func (h *Header) AddSheet(name string) {
method AddXRes (line 94) | func (h *Header) AddXRes(names ...string) {
method AddNotice (line 113) | func (h *Header) AddNotice(name string) {
type HScript (line 56) | type HScript struct
type HeaderLite (line 118) | type HeaderLite struct
type PageWidgets (line 125) | type PageWidgets struct
type ExtData (line 132) | type ExtData struct
type Page (line 137) | type Page struct
type SimplePage (line 143) | type SimplePage struct
type ErrorPage (line 147) | type ErrorPage struct
type Paginator (line 152) | type Paginator struct
type PaginatorMod (line 158) | type PaginatorMod struct
type CustomPagePage (line 165) | type CustomPagePage struct
type TopicCEditPost (line 170) | type TopicCEditPost struct
type TopicCAttachItem (line 175) | type TopicCAttachItem struct
type TopicCPollInput (line 181) | type TopicCPollInput struct
type TopicPage (line 186) | type TopicPage struct
type TopicListSort (line 195) | type TopicListSort struct
type QuickTools (line 200) | type QuickTools struct
type TopicListPage (line 206) | type TopicListPage struct
type ForumPage (line 217) | type ForumPage struct
type ForumsPage (line 226) | type ForumsPage struct
type ProfilePage (line 231) | type ProfilePage struct
type CreateTopicPage (line 243) | type CreateTopicPage struct
type IPSearchPage (line 249) | type IPSearchPage struct
type RegisterVerifyImageGridImage (line 256) | type RegisterVerifyImageGridImage struct
type RegisterVerifyImageGrid (line 259) | type RegisterVerifyImageGrid struct
type RegisterVerify (line 263) | type RegisterVerify struct
type RegisterPage (line 269) | type RegisterPage struct
type Account (line 276) | type Account struct
type EmailListPage (line 283) | type EmailListPage struct
type AccountLoginsPage (line 288) | type AccountLoginsPage struct
type AccountBlocksPage (line 294) | type AccountBlocksPage struct
type AccountPrivacyPage (line 300) | type AccountPrivacyPage struct
type AccountDashPage (line 307) | type AccountDashPage struct
type LevelListItem (line 316) | type LevelListItem struct
type LevelListPage (line 323) | type LevelListPage struct
type ResetPage (line 328) | type ResetPage struct
type ConvoListRow (line 335) | type ConvoListRow struct
type ConvoListPage (line 341) | type ConvoListPage struct
type ConvoViewRow (line 347) | type ConvoViewRow struct
type ConvoViewPage (line 356) | type ConvoViewPage struct
type ConvoCreatePage (line 365) | type ConvoCreatePage struct
type Panel (line 371) | type Panel struct
type PanelAnalytics (line 378) | type PanelAnalytics struct
type PanelAnalyticsStd (line 384) | type PanelAnalyticsStd struct
type PanelAnalyticsStdUnit (line 391) | type PanelAnalyticsStdUnit struct
type PanelAnalyticsActiveMemory (line 398) | type PanelAnalyticsActiveMemory struct
type PanelAnalyticsPerf (line 406) | type PanelAnalyticsPerf struct
type PanelStats (line 415) | type PanelStats struct
type BasePanelPage (line 425) | type BasePanelPage struct
type PanelPage (line 432) | type PanelPage struct
type GridElement (line 438) | type GridElement struct
type DashGrids (line 448) | type DashGrids struct
type PanelDashboardPage (line 452) | type PanelDashboardPage struct
type PanelSetting (line 457) | type PanelSetting struct
type PanelSettingPage (line 461) | type PanelSettingPage struct
type PanelUserEditPage (line 467) | type PanelUserEditPage struct
type PanelCustomPagesPage (line 474) | type PanelCustomPagesPage struct
type PanelCustomPageEditPage (line 479) | type PanelCustomPageEditPage struct
type PanelTimeGraph (line 488) | type PanelTimeGraph struct
type PanelAnalyticsItem (line 494) | type PanelAnalyticsItem struct
type PanelAnalyticsItemUnit (line 498) | type PanelAnalyticsItemUnit struct
type PanelAnalyticsPage (line 504) | type PanelAnalyticsPage struct
type PanelAnalyticsRoutesItem (line 513) | type PanelAnalyticsRoutesItem struct
type PanelAnalyticsRoutesPage (line 518) | type PanelAnalyticsRoutesPage struct
type PanelAnalyticsRoutesPerfItem (line 525) | type PanelAnalyticsRoutesPerfItem struct
type PanelAnalyticsRoutesPerfPage (line 531) | type PanelAnalyticsRoutesPerfPage struct
type PanelAnalyticsAgentsItem (line 539) | type PanelAnalyticsAgentsItem struct
type PanelAnalyticsAgentsPage (line 545) | type PanelAnalyticsAgentsPage struct
type PanelAnalyticsReferrersPage (line 551) | type PanelAnalyticsReferrersPage struct
type PanelAnalyticsRoutePage (line 558) | type PanelAnalyticsRoutePage struct
type PanelAnalyticsAgentPage (line 566) | type PanelAnalyticsAgentPage struct
type PanelAnalyticsDuoPage (line 574) | type PanelAnalyticsDuoPage struct
type PanelThemesPage (line 581) | type PanelThemesPage struct
type PanelMenuListItem (line 587) | type PanelMenuListItem struct
type PanelMenuListPage (line 593) | type PanelMenuListPage struct
type PanelWidgetListPage (line 598) | type PanelWidgetListPage struct
type PanelMenuPage (line 604) | type PanelMenuPage struct
type PanelMenuItemPage (line 610) | type PanelMenuItemPage struct
type PanelUserPageSearch (line 615) | type PanelUserPageSearch struct
type PanelUserPage (line 622) | type PanelUserPage struct
type PanelGroupPage (line 630) | type PanelGroupPage struct
type PanelEditGroupPage (line 636) | type PanelEditGroupPage struct
type GroupForumPermPreset (line 645) | type GroupForumPermPreset struct
type PanelEditForumPage (line 651) | type PanelEditForumPage struct
type ForumActionAction (line 662) | type ForumActionAction struct
type NameLangToggle (line 667) | type NameLangToggle struct
type PanelEditForumGroupPage (line 673) | type PanelEditForumGroupPage struct
type PanelEditGroupPermsPage (line 684) | type PanelEditGroupPermsPage struct
type GroupPromotionExtend (line 693) | type GroupPromotionExtend struct
type PanelEditGroupPromotionsPage (line 699) | type PanelEditGroupPromotionsPage struct
type BackupItem (line 707) | type BackupItem struct
type PanelBackupPage (line 715) | type PanelBackupPage struct
type PageLogItem (line 720) | type PageLogItem struct
type PanelLogsPage (line 726) | type PanelLogsPage struct
type PageRegLogItem (line 732) | type PageRegLogItem struct
type PanelRegLogsPage (line 737) | type PanelRegLogsPage struct
type DebugPageTasks (line 743) | type DebugPageTasks struct
type DebugPageCache (line 752) | type DebugPageCache struct
type DebugPageDatabase (line 764) | type DebugPageDatabase struct
type DebugPageDisk (line 789) | type DebugPageDisk struct
type PanelDebugPage (line 798) | type PanelDebugPage struct
type PanelTaskTask (line 818) | type PanelTaskTask struct
type PanelTaskType (line 822) | type PanelTaskType struct
type PanelTaskPage (line 826) | type PanelTaskPage struct
type PageSimple (line 832) | type PageSimple struct
type AreYouSure (line 837) | type AreYouSure struct
function DefaultHeader (line 843) | func DefaultHeader(w http.ResponseWriter, u *User) *Header {
function SimpleDefaultHeader (line 846) | func SimpleDefaultHeader(w http.ResponseWriter) *Header {
FILE: common/parser.go
constant imageSizeHint (line 57) | imageSizeHint = len("<a href=\"") + len("\"><img src='") + len("'class='...
constant videoSizeHint (line 58) | videoSizeHint = len("<video controls src=\"") + len("\"><a class='attach...
constant audioSizeHint (line 59) | audioSizeHint = len("<audio controls src=\"") + len("\"><a class='attach...
constant mentionSizeHint (line 60) | mentionSizeHint = len("<a href='") + len("'class='mention'") + len(">@")...
function init (line 62) | func init() {
type emojiHolder (line 68) | type emojiHolder struct
function InitEmoji (line 73) | func InitEmoji() error {
function shortcodeToUnicode (line 108) | func shortcodeToUnicode(msg string) string {
type TagToAction (line 116) | type TagToAction struct
function tryStepForward (line 124) | func tryStepForward(i, step int, runes []rune) (int, bool) {
function tryStepBackward (line 133) | func tryStepBackward(i, step int, runes []rune) (int, bool) {
function PreparseMessage (line 142) | func PreparseMessage(msg string) string {
function peek (line 388) | func peek(cur, skip int, runes []rune) rune {
function peekMatch (line 396) | func peekMatch(cur int, phrase string, runes []rune) bool {
function AddHashLinkType (line 412) | func AddHashLinkType(prefix string, h func(*strings.Builder, string, *in...
function WriteURL (line 422) | func WriteURL(sb *strings.Builder, url, label string) {
type ParseSettings (line 471) | type ParseSettings struct
method CopyPtr (line 475) | func (ps *ParseSettings) CopyPtr() *ParseSettings {
function ParseMessage (line 481) | func ParseMessage(msg string, sectionID int, sectionType string, setting...
function ParseMessage2 (line 494) | func ParseMessage2(msg string, sectionID int, sectionType string, settin...
function validateURLString (line 874) | func validateURLString(d string) bool {
function validatedURLBytes (line 899) | func validatedURLBytes(data []byte) (url []byte) {
function PartialURLString (line 927) | func PartialURLString(d string) (url []byte) {
function PartialURLStringLen (line 956) | func PartialURLStringLen(d string) (int, bool) {
function PartialURLStringLen2 (line 1001) | func PartialURLStringLen2(d string) int {
type MediaEmbed (line 1028) | type MediaEmbed struct
constant ENone (line 1039) | ENone = iota
constant ERaw (line 1040) | ERaw
constant ERawExternal (line 1041) | ERawExternal
constant EImage (line 1042) | EImage
constant AImage (line 1043) | AImage
constant AVideo (line 1044) | AVideo
constant AAudio (line 1045) | AAudio
constant AText (line 1046) | AText
constant AOther (line 1047) | AOther
function parseMediaString (line 1053) | func parseMediaString(data string, settings *ParseSettings) (media Media...
function parseDuration (line 1246) | func parseDuration(dur string) (s, m, h int) {
function CoerceIntString (line 1271) | func CoerceIntString(data string) (res, length int) {
function Paginate (line 1295) | func Paginate(currentPage, lastPage, maxPages int) (out []int) {
function PageOffset (line 1315) | func PageOffset(count, page, perPage int) (int, int, int) {
function LastPage (line 1337) | func LastPage(count, perPage int) int {
FILE: common/password_reset.go
type DefaultPasswordResetter (line 14) | type DefaultPasswordResetter struct
method Create (line 41) | func (r *DefaultPasswordResetter) Create(email string, uid int, token ...
method FlushTokens (line 46) | func (r *DefaultPasswordResetter) FlushTokens(uid int) error {
method ValidateToken (line 51) | func (r *DefaultPasswordResetter) ValidateToken(uid int, token string)...
function NewDefaultPasswordResetter (line 30) | func NewDefaultPasswordResetter(acc *qgen.Accumulator) (*DefaultPassword...
FILE: common/permissions.go
type Perms (line 50) | type Perms struct
function init (line 102) | func init() {
function StripInvalidGroupForumPreset (line 161) | func StripInvalidGroupForumPreset(preset string) string {
function StripInvalidPreset (line 169) | func StripInvalidPreset(preset string) string {
function PresetToLang (line 178) | func PresetToLang(preset string) string {
function RebuildGroupPermissions (line 189) | func RebuildGroupPermissions(g *Group) error {
function OverridePerms (line 216) | func OverridePerms(p *Perms, status bool) {
function OverrideForumPerms (line 225) | func OverrideForumPerms(p *Perms, status bool) {
function RegisterPluginPerm (line 239) | func RegisterPluginPerm(name string) {
function DeregisterPluginPerm (line 243) | func DeregisterPluginPerm(name string) {
FILE: common/phrases/phrases.go
type LevelPhrases (line 31) | type LevelPhrases struct
type LanguagePack (line 40) | type LanguagePack struct
function InitPhrases (line 69) | func InitPhrases(lang string) error {
function LoadLangPack (line 167) | func LoadLangPack(name string) error {
function SaveLangPack (line 173) | func SaveLangPack(langPack *LanguagePack) error {
function GetLangPack (line 178) | func GetLangPack() *LanguagePack {
function GetLevelPhrase (line 182) | func GetLevelPhrase(level int) string {
function GetPermPhrase (line 190) | func GetPermPhrase(name string) string {
function GetSettingPhrase (line 198) | func GetSettingPhrase(name string) string {
function GetAllSettingPhrases (line 206) | func GetAllSettingPhrases() map[string]string {
function GetAllPermPresets (line 210) | func GetAllPermPresets() map[string]string {
function GetAccountPhrase (line 214) | func GetAccountPhrase(name string) string {
function GetUserAgentPhrase (line 222) | func GetUserAgentPhrase(name string) (string, bool) {
function GetOSPhrase (line 230) | func GetOSPhrase(name string) (string, bool) {
function GetHumanLangPhrase (line 238) | func GetHumanLangPhrase(name string) (string, bool) {
function GetErrorPhrase (line 247) | func GetErrorPhrase(name string) string {
function GetErrorPhraseBytes (line 254) | func GetErrorPhraseBytes(name string) []byte {
function GetNoticePhrase (line 262) | func GetNoticePhrase(name string) string {
function GetTitlePhrase (line 270) | func GetTitlePhrase(name string) string {
function GetTitlePhrasef (line 278) | func GetTitlePhrasef(name string, params ...interface{}) string {
function GetTmplPhrase (line 286) | func GetTmplPhrase(name string) string {
function GetTmplPhrasef (line 294) | func GetTmplPhrasef(name string, params ...interface{}) string {
function GetTmplPhrases (line 302) | func GetTmplPhrases() map[string]string {
function GetTmplPhrasesByPrefix (line 306) | func GetTmplPhrasesByPrefix(prefix string) (phrases map[string]string, o...
function getPlaceholder (line 311) | func getPlaceholder(prefix, suffix string) string {
function getPlaceholderBytes (line 314) | func getPlaceholderBytes(prefix, suffix string) []byte {
function GetCurrentLangPack (line 319) | func GetCurrentLangPack() *LanguagePack {
function AddPhrase (line 325) | func AddPhrase() {
function UpdatePhrase (line 328) | func UpdatePhrase() {
function DeletePhrase (line 331) | func DeletePhrase() {
function ChangeLanguagePack (line 337) | func ChangeLanguagePack(name string) (exists bool) {
function CurrentLanguagePackName (line 346) | func CurrentLanguagePackName() (name string) {
function GetLanguagePackByName (line 350) | func GetLanguagePackByName(name string) (pack *LanguagePack, ok bool) {
function RegisterTmplPhraseNames (line 360) | func RegisterTmplPhraseNames(phraseNames []string) (tmplID int) {
function GetTmplPhrasesBytes (line 365) | func GetTmplPhrasesBytes(tmplID int) [][]byte {
function TmplIndexCallback (line 373) | func TmplIndexCallback(tmplID int, phraseSet [][]byte) {
function AddTmplIndexCallback (line 377) | func AddTmplIndexCallback(h func([][]byte)) {
FILE: common/pluginlangs.go
type PluginMeta (line 13) | type PluginMeta struct
type PluginLang (line 26) | type PluginLang interface
function InitPluginLangs (line 45) | func InitPluginLangs() error {
function GetPluginFiles (line 99) | func GetPluginFiles() (pluginList []string, err error) {
function ExtToPluginLang (line 113) | func ExtToPluginLang(ext string) (PluginLang, error) {
FILE: common/poll.go
type Poll (line 11) | type Poll struct
method CastVote (line 27) | func (p *Poll) CastVote(optionIndex, uid int, ip string) error {
method Delete (line 43) | func (p *Poll) Delete() error {
method Resultsf (line 57) | func (p *Poll) Resultsf(f func(votes int) error) error {
method Copy (line 76) | func (p *Poll) Copy() Poll {
type PollStmts (line 80) | type PollStmts struct
function init (line 92) | func init() {
FILE: common/poll_cache.go
type PollCache (line 9) | type PollCache interface
type MemoryPollCache (line 25) | type MemoryPollCache struct
method Get (line 42) | func (s *MemoryPollCache) Get(id int) (*Poll, error) {
method BulkGet (line 53) | func (s *MemoryPollCache) BulkGet(ids []int) (list []*Poll) {
method GetUnsafe (line 64) | func (s *MemoryPollCache) GetUnsafe(id int) (*Poll, error) {
method Set (line 73) | func (s *MemoryPollCache) Set(item *Poll) error {
method Add (line 92) | func (s *MemoryPollCache) Add(item *Poll) error {
method AddUnsafe (line 105) | func (s *MemoryPollCache) AddUnsafe(item *Poll) error {
method Remove (line 115) | func (s *MemoryPollCache) Remove(id int) error {
method RemoveUnsafe (line 129) | func (s *MemoryPollCache) RemoveUnsafe(id int) error {
method Flush (line 140) | func (s *MemoryPollCache) Flush() {
method Length (line 150) | func (s *MemoryPollCache) Length() int {
method SetCapacity (line 155) | func (s *MemoryPollCache) SetCapacity(capacity int) {
method GetCapacity (line 161) | func (s *MemoryPollCache) GetCapacity() int {
function NewMemoryPollCache (line 34) | func NewMemoryPollCache(capacity int) *MemoryPollCache {
type NullPollCache (line 166) | type NullPollCache struct
method Get (line 175) | func (s *NullPollCache) Get(id int) (*Poll, error) {
method BulkGet (line 178) | func (s *NullPollCache) BulkGet(ids []int) (list []*Poll) {
method GetUnsafe (line 181) | func (s *NullPollCache) GetUnsafe(id int) (*Poll, error) {
method Set (line 184) | func (s *NullPollCache) Set(_ *Poll) error {
method Add (line 187) | func (s *NullPollCache) Add(_ *Poll) error {
method AddUnsafe (line 190) | func (s *NullPollCache) AddUnsafe(_ *Poll) error {
method Remove (line 193) | func (s *NullPollCache) Remove(id int) error {
method RemoveUnsafe (line 196) | func (s *NullPollCache) RemoveUnsafe(id int) error {
method Flush (line 199) | func (s *NullPollCache) Flush() {
method Length (line 201) | func (s *NullPollCache) Length() int {
method SetCapacity (line 204) | func (s *NullPollCache) SetCapacity(_ int) {
method GetCapacity (line 206) | func (s *NullPollCache) GetCapacity() int {
function NewNullPollCache (line 170) | func NewNullPollCache() *NullPollCache {
FILE: common/poll_store.go
type PollOption (line 15) | type PollOption struct
type Pollable (line 20) | type Pollable interface
type PollStore (line 26) | type PollStore interface
type DefaultPollStore (line 38) | type DefaultPollStore struct
method Exists (line 70) | func (s *DefaultPollStore) Exists(id int) bool {
method Get (line 78) | func (s *DefaultPollStore) Get(id int) (*Poll, error) {
method BulkGetMap (line 101) | func (s *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, ...
method Reload (line 176) | func (s *DefaultPollStore) Reload(id int) error {
method unpackOptionsMap (line 194) | func (s *DefaultPollStore) unpackOptionsMap(rawOptions map[int]string)...
method ClearIPs (line 202) | func (s *DefaultPollStore) ClearIPs() error {
method Create (line 208) | func (s *DefaultPollStore) Create(parent Pollable, pollType int, pollO...
method Count (line 234) | func (s *DefaultPollStore) Count() int {
method SetCache (line 238) | func (s *DefaultPollStore) SetCache(cache PollCache) {
method GetCache (line 243) | func (s *DefaultPollStore) GetCache() PollCache {
function NewDefaultPollStore (line 51) | func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
FILE: common/profile_reply.go
type ProfileReply (line 14) | type ProfileReply struct
method Delete (line 49) | func (r *ProfileReply) Delete() error {
method SetBody (line 66) | func (r *ProfileReply) SetBody(content string) error {
method Creator (line 73) | func (r *ProfileReply) Creator() (*User, error) {
type ProfileReplyStmts (line 27) | type ProfileReplyStmts struct
function init (line 32) | func init() {
function BlankProfileReply (line 44) | func BlankProfileReply(id int) *ProfileReply {
FILE: common/profile_reply_store.go
type ProfileReplyStore (line 11) | type ProfileReplyStore interface
type SQLProfileReplyStore (line 21) | type SQLProfileReplyStore struct
method Get (line 42) | func (s *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) {
method Exists (line 48) | func (s *SQLProfileReplyStore) Exists(id int) bool {
method ClearIPs (line 56) | func (s *SQLProfileReplyStore) ClearIPs() error {
method Create (line 61) | func (s *SQLProfileReplyStore) Create(profileID int, content string, c...
method Count (line 79) | func (s *SQLProfileReplyStore) Count() (count int) {
function NewSQLProfileReplyStore (line 30) | func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplySto...
FILE: common/promotions.go
type GroupPromotion (line 13) | type GroupPromotion struct
type GroupPromotionStore (line 25) | type GroupPromotionStore interface
type DefaultGroupPromotionStore (line 33) | type DefaultGroupPromotionStore struct
method Tick (line 62) | func (s *DefaultGroupPromotionStore) Tick() error {
method GetByGroup (line 66) | func (s *DefaultGroupPromotionStore) GetByGroup(gid int) (gps []*Group...
method Get (line 85) | func (s *DefaultGroupPromotionStore) Get(id int) (*GroupPromotion, err...
method PromoteIfEligible (line 100) | func (s *DefaultGroupPromotionStore) PromoteIfEligible(u *User, level,...
method Delete (line 120) | func (s *DefaultGroupPromotionStore) Delete(id int) error {
method Create (line 125) | func (s *DefaultGroupPromotionStore) Create(from, to int, twoWay bool,...
function NewDefaultGroupPromotionStore (line 45) | func NewDefaultGroupPromotionStore(acc *qgen.Accumulator) (*DefaultGroup...
FILE: common/ratelimit.go
type RateLimiter (line 14) | type RateLimiter interface
type RateData (line 19) | type RateData struct
type RateFence (line 24) | type RateFence struct
type RateLimit (line 30) | type RateLimit struct
method Limit (line 44) | func (l *RateLimit) Limit(name string, ltype int) error {
function NewRateLimit (line 37) | func NewRateLimit(fences []RateFence) *RateLimit {
type DefaultRateLimiter (line 76) | type DefaultRateLimiter struct
method LimitIP (line 86) | func (l *DefaultRateLimiter) LimitIP(limit, ip string) error {
method LimitUser (line 94) | func (l *DefaultRateLimiter) LimitUser(limit string, user int) error {
function NewDefaultRateLimiter (line 80) | func NewDefaultRateLimiter() *DefaultRateLimiter {
FILE: common/recalc.go
type RecalcInt (line 13) | type RecalcInt interface
type DefaultRecalc (line 22) | type DefaultRecalc struct
method Replies (line 42) | func (s *DefaultRecalc) Replies() (count int, err error) {
method Forums (line 60) | func (s *DefaultRecalc) Forums() (count int, err error) {
method Subscriptions (line 72) | func (s *DefaultRecalc) Subscriptions() (count int, err error) {
method ActivityStream (line 99) | func (s *DefaultRecalc) ActivityStream() (count int, err error) {
method Users (line 162) | func (s *DefaultRecalc) Users() error {
method Attachments (line 168) | func (s *DefaultRecalc) Attachments() (count int, err error) {
function NewDefaultRecalc (line 30) | func NewDefaultRecalc(acc *qgen.Accumulator) (*DefaultRecalc, error) {
type Existable (line 95) | type Existable interface
FILE: common/relations.go
type BlockStore (line 13) | type BlockStore interface
type DefaultBlockStore (line 22) | type DefaultBlockStore struct
method IsBlockedBy (line 41) | func (s *DefaultBlockStore) IsBlockedBy(blocker, blockee int) (bool, e...
method BulkIsBlockedBy (line 50) | func (s *DefaultBlockStore) BulkIsBlockedBy(blockers []int, blockee in...
method Add (line 65) | func (s *DefaultBlockStore) Add(blocker, blockee int) error {
method Remove (line 70) | func (s *DefaultBlockStore) Remove(blocker, blockee int) error {
method BlockedByOffset (line 75) | func (s *DefaultBlockStore) BlockedByOffset(blocker, offset, perPage i...
method BlockedByCount (line 91) | func (s *DefaultBlockStore) BlockedByCount(blocker int) (count int) {
function NewDefaultBlockStore (line 30) | func NewDefaultBlockStore(acc *qgen.Accumulator) (*DefaultBlockStore, er...
type FriendInvite (line 99) | type FriendInvite struct
type FriendStore (line 104) | type FriendStore interface
type DefaultFriendStore (line 111) | type DefaultFriendStore struct
method AddInvite (line 128) | func (s *DefaultFriendStore) AddInvite(requester, target int) error {
method Confirm (line 133) | func (s *DefaultFriendStore) Confirm(requester, target int) error {
method GetOwnSentInvites (line 138) | func (s *DefaultFriendStore) GetOwnSentInvites(uid int) ([]FriendInvit...
method GetOwnRecvInvites (line 141) | func (s *DefaultFriendStore) GetOwnRecvInvites(uid int) ([]FriendInvit...
function NewDefaultFriendStore (line 118) | func NewDefaultFriendStore(acc *qgen.Accumulator) (*DefaultFriendStore, ...
FILE: common/reply.go
type ReplyUser (line 19) | type ReplyUser struct
type Reply (line 40) | type Reply struct
method Like (line 105) | func (r *Reply) Like(uid int) (err error) {
method Unlike (line 129) | func (r *Reply) Unlike(uid int) error {
method Delete (line 144) | func (r *Reply) Delete() error {
method SetPost (line 197) | func (r *Reply) SetPost(content string) error {
method SetPoll (line 210) | func (r *Reply) SetPoll(pollID int) error {
method Topic (line 216) | func (r *Reply) Topic() (*Topic, error) {
method GetID (line 220) | func (r *Reply) GetID() int {
method GetTable (line 224) | func (r *Reply) GetTable() string {
method Copy (line 229) | func (r *Reply) Copy() Reply {
type ReplyStmts (line 60) | type ReplyStmts struct
function init (line 78) | func init() {
FILE: common/reply_cache.go
type ReplyCache (line 10) | type ReplyCache interface
type MemoryReplyCache (line 26) | type MemoryReplyCache struct
method Get (line 43) | func (s *MemoryReplyCache) Get(id int) (*Reply, error) {
method GetUnsafe (line 54) | func (s *MemoryReplyCache) GetUnsafe(id int) (*Reply, error) {
method BulkGet (line 63) | func (s *MemoryReplyCache) BulkGet(ids []int) (list []*Reply) {
method Set (line 74) | func (s *MemoryReplyCache) Set(item *Reply) error {
method Add (line 92) | func (s *MemoryReplyCache) Add(item *Reply) error {
method AddUnsafe (line 106) | func (s *MemoryReplyCache) AddUnsafe(item *Reply) error {
method Remove (line 116) | func (s *MemoryReplyCache) Remove(id int) error {
method RemoveUnsafe (line 130) | func (s *MemoryReplyCache) RemoveUnsafe(id int) error {
method Flush (line 141) | func (s *MemoryReplyCache) Flush() {
method Length (line 150) | func (s *MemoryReplyCache) Length() int {
method SetCapacity (line 155) | func (s *MemoryReplyCache) SetCapacity(cap int) {
method GetCapacity (line 161) | func (s *MemoryReplyCache) GetCapacity() int {
function NewMemoryReplyCache (line 35) | func NewMemoryReplyCache(cap int) *MemoryReplyCache {
FILE: common/reply_store.go
type ReplyStore (line 12) | type ReplyStore interface
type SQLReplyStore (line 27) | type SQLReplyStore struct
method Get (line 60) | func (s *SQLReplyStore) Get(id int) (*Reply, error) {
method Each (line 78) | func (s *SQLReplyStore) Each(f func(*Reply) error) error {
method Exists (line 96) | func (s *SQLReplyStore) Exists(id int) bool {
method ClearIPs (line 104) | func (s *SQLReplyStore) ClearIPs() error {
method Create (line 110) | func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) ...
method Count (line 128) | func (s *SQLReplyStore) Count() (count int) {
method CountUser (line 131) | func (s *SQLReplyStore) CountUser(uid int) (count int) {
method CountMegaUser (line 134) | func (s *SQLReplyStore) CountMegaUser(uid int) (count int) {
method CountBigUser (line 137) | func (s *SQLReplyStore) CountBigUser(uid int) (count int) {
method SetCache (line 141) | func (s *SQLReplyStore) SetCache(cache ReplyCache) {
method GetCache (line 145) | func (s *SQLReplyStore) GetCache() ReplyCache {
function NewSQLReplyStore (line 41) | func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLRepl...
FILE: common/report_store.go
constant ReportForumID (line 13) | ReportForumID = 1
type ReportStore (line 19) | type ReportStore interface
type DefaultReportStore (line 23) | type DefaultReportStore struct
method Create (line 37) | func (s *DefaultReportStore) Create(title, content string, u *User, it...
function NewDefaultReportStore (line 28) | func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, ...
FILE: common/routes_common.go
function simpleForumUserCheck (line 32) | func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, u *Use...
function forumUserCheck (line 61) | func forumUserCheck(h *Header, w http.ResponseWriter, r *http.Request, u...
function cascadeForumPerms (line 91) | func cascadeForumPerms(fp *ForumPerms, u *User) {
function panelUserCheck (line 115) | func panelUserCheck(w http.ResponseWriter, r *http.Request, u *User) (h ...
function simplePanelUserCheck (line 190) | func simplePanelUserCheck(w http.ResponseWriter, r *http.Request, u *Use...
function simpleUserCheck (line 195) | func simpleUserCheck(w http.ResponseWriter, r *http.Request, u *User) (l...
function GetThemeByReq (line 203) | func GetThemeByReq(r *http.Request) *Theme {
function userCheck (line 218) | func userCheck(w http.ResponseWriter, r *http.Request, u *User) (h *Head...
function userCheck2 (line 224) | func userCheck2(w http.ResponseWriter, r *http.Request, u *User, nano in...
function PrepResources (line 269) | func PrepResources(u *User, h *Header, theme *Theme) {
function pstr (line 318) | func pstr(name string) string {
function preRoute (line 335) | func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) {
function UploadAvatar (line 388) | func UploadAvatar(w http.ResponseWriter, r *http.Request, u *User, tuid ...
function ChangeAvatar (line 454) | func ChangeAvatar(path string, w http.ResponseWriter, r *http.Request, u...
function SuperAdminOnly (line 480) | func SuperAdminOnly(w http.ResponseWriter, r *http.Request, u *User) Rou...
function AdminOnly (line 488) | func AdminOnly(w http.ResponseWriter, r *http.Request, u *User) RouteErr...
function SuperModOnly (line 496) | func SuperModOnly(w http.ResponseWriter, r *http.Request, u *User) Route...
function MemberOnly (line 504) | func MemberOnly(w http.ResponseWriter, r *http.Request, u *User) RouteEr...
function NoBanned (line 512) | func NoBanned(w http.ResponseWriter, r *http.Request, u *User) RouteError {
function ParseForm (line 519) | func ParseForm(w http.ResponseWriter, r *http.Request, u *User) RouteErr...
function NoSessionMismatch (line 526) | func NoSessionMismatch(w http.ResponseWriter, r *http.Request, u *User) ...
function ReqIsJson (line 541) | func ReqIsJson(r *http.Request) bool {
function HandleUploadRoute (line 545) | func HandleUploadRoute(w http.ResponseWriter, r *http.Request, u *User, ...
function NoUploadSessionMismatch (line 560) | func NoUploadSessionMismatch(w http.ResponseWriter, r *http.Request, u *...
FILE: common/search.go
type Searcher (line 13) | type Searcher interface
type SQLSearcher (line 19) | type SQLSearcher struct
method queryAll (line 44) | func (s *SQLSearcher) queryAll(q string) ([]int, error) {
method Query (line 79) | func (s *SQLSearcher) Query(q string, zones []int) (ids []int, err err...
function NewSQLSearcher (line 30) | func NewSQLSearcher(acc *qgen.Accumulator) (*SQLSearcher, error) {
type ElasticSearchSearcher (line 146) | type ElasticSearchSearcher struct
method Query (line 153) | func (s *ElasticSearchSearcher) Query(q string, zones []int) ([]int, e...
function NewElasticSearchSearcher (line 149) | func NewElasticSearchSearcher() (*ElasticSearchSearcher, error) {
FILE: common/settings.go
type SettingMap (line 16) | type SettingMap
method ParseSetting (line 83) | func (sBox SettingMap) ParseSetting(name, content, typ, constraint str...
method BypassGet (line 125) | func (sBox SettingMap) BypassGet(name string) (*Setting, error) {
method BypassGetAll (line 131) | func (sBox SettingMap) BypassGetAll() (settingList []*Setting, err err...
method Update (line 149) | func (sBox SettingMap) Update(name, content string) RouteError {
type SettingStore (line 18) | type SettingStore interface
type OptionLabel (line 24) | type OptionLabel struct
type Setting (line 30) | type Setting struct
method Copy (line 58) | func (s *Setting) Copy() (o *Setting) {
type SettingStmts (line 37) | type SettingStmts struct
function init (line 45) | func init() {
function LoadSettings (line 64) | func LoadSettings() error {
FILE: common/site.go
type site (line 28) | type site struct
type dbConfig (line 45) | type dbConfig struct
type config (line 63) | type config struct
type devConfig (line 147) | type devConfig struct
type configHolder (line 169) | type configHolder struct
function LoadConfig (line 177) | func LoadConfig() error {
function ProcessConfig (line 206) | func ProcessConfig() (err error) {
function VerifyConfig (line 364) | func VerifyConfig() (err error) {
function SwitchToTestDB (line 378) | func SwitchToTestDB() {
FILE: common/statistics.go
type StatStoreInt (line 10) | type StatStoreInt interface
type DefaultStatStore (line 14) | type DefaultStatStore struct
method LookupInt (line 21) | func (s *DefaultStatStore) LookupInt(name string, duration int, unit s...
method countTable (line 29) | func (s *DefaultStatStore) countTable(table string, duration int, unit...
function NewDefaultStatStore (line 17) | func NewDefaultStatStore() *DefaultStatStore {
FILE: common/subscription.go
type SubscriptionStore (line 12) | type SubscriptionStore interface
type DefaultSubscriptionStore (line 18) | type DefaultSubscriptionStore struct
method Add (line 34) | func (s *DefaultSubscriptionStore) Add(uid, elementID int, elementType...
method Delete (line 40) | func (s *DefaultSubscriptionStore) Delete(uid, targetID int, targetTyp...
method DeleteResource (line 45) | func (s *DefaultSubscriptionStore) DeleteResource(targetID int, target...
function NewDefaultSubscriptionStore (line 24) | func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) {
FILE: common/tasks.go
type TaskStmts (line 17) | type TaskStmts struct
type TaskSet (line 24) | type TaskSet interface
type DefaultTaskSet (line 31) | type DefaultTaskSet struct
method Add (line 35) | func (s *DefaultTaskSet) Add(task func() error) {
method GetList (line 39) | func (s *DefaultTaskSet) GetList() []func() error {
method Run (line 43) | func (s *DefaultTaskSet) Run() error {
method Count (line 52) | func (s *DefaultTaskSet) Count() int {
type ScheduledTasks (line 56) | type ScheduledTasks struct
function NewScheduledTasks (line 65) | func NewScheduledTasks() *ScheduledTasks {
function init (line 86) | func init() {
function HandleExpiredScheduledGroups (line 158) | func HandleExpiredScheduledGroups() error {
function HandleServerSync (line 184) | func HandleServerSync() error {
FILE: common/template_init.go
function skipCTmpl (line 30) | func skipCTmpl(key string) bool {
type CTmpl (line 39) | type CTmpl struct
function genIntTmpl (line 48) | func genIntTmpl(name string) func(pi interface{}, w io.Writer) error {
function tmplInitUsers (line 96) | func tmplInitUsers() (*User, *User, *User) {
function tmplInitHeaders (line 109) | func tmplInitHeaders(u, u2, u3 *User) (*Header, *Header, *Header) {
type TmplLoggedin (line 137) | type TmplLoggedin struct
type nobreak (line 143) | type nobreak interface
type TItem (line 145) | type TItem struct
type TItemHold (line 151) | type TItemHold
method Add (line 153) | func (h TItemHold) Add(name, expects string, expectsInt interface{}) {
method AddStd (line 157) | func (h TItemHold) AddStd(name, expects string, expectsInt interface{}) {
function CompileTemplates (line 162) | func CompileTemplates() error {
function compileCommons (line 215) | func compileCommons(c *tmpl.CTemplateSet, head, head2 *Header, forumList...
function compileTemplates (line 267) | func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeNam...
function CompileJSTemplates (line 461) | func CompileJSTemplates() error {
function compileJSTemplates (line 515) | func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeN...
function getTemplateList (line 630) | func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix st...
function writeTemplateList (line 739) | func writeTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix ...
function arithToInt64 (line 753) | func arithToInt64(in interface{}) (o int64) {
function arithDuoToInt64 (line 773) | func arithDuoToInt64(left, right interface{}) (leftInt, rightInt int64) {
function initDefaultTmplFuncMap (line 777) | func initDefaultTmplFuncMap() {
function loadTemplates (line 922) | func loadTemplates(t *template.Template, themeName string) error {
function InitTemplates (line 1009) | func InitTemplates() error {
FILE: common/templates/context.go
type FragLite (line 8) | type FragLite struct
type Fragment (line 12) | type Fragment struct
type OutBufferFrame (line 19) | type OutBufferFrame struct
type CContext (line 27) | type CContext struct
method Push (line 37) | func (con *CContext) Push(nType string, body string) (index int) {
method PushText (line 42) | func (con *CContext) PushText(body string, fragIndex int, fragOutIndex...
method PushPhrase (line 47) | func (con *CContext) PushPhrase(langIndex int) (index int) {
method PushPhrasef (line 52) | func (con *CContext) PushPhrasef(langIndex int, args string) (index in...
method StartIf (line 57) | func (con *CContext) StartIf(body string) (index int) {
method StartIfPtr (line 61) | func (con *CContext) StartIfPtr(body string) (index int) {
method EndIf (line 66) | func (con *CContext) EndIf(startIndex int, body string) (index int) {
method StartLoop (line 71) | func (con *CContext) StartLoop(body string) (index int) {
method EndLoop (line 76) | func (con *CContext) EndLoop(body string) (index int) {
method StartTemplate (line 80) | func (con *CContext) StartTemplate(body string) (index int) {
method EndTemplate (line 84) | func (con *CContext) EndTemplate(body string) (index int) {
method AttachVars (line 88) | func (con *CContext) AttachVars(vars string, index int) {
method addFrame (line 98) | func (con *CContext) addFrame(body, ftype string, extra1 interface{}, ...
method LastBufIndex (line 103) | func (con *CContext) LastBufIndex() int {
method DiscardAndAfter (line 107) | func (con *CContext) DiscardAndAfter(index int) {
FILE: common/templates/minifiers.go
function Minify (line 9) | func Minify(data string) string {
function minifyHTML (line 21) | func minifyHTML(data string) string {
function minifyCSS (line 29) | func minifyCSS(data string) string {
function rgbToHexstr (line 36) | func rgbToHexstr(red, green, blue int) string {
FILE: common/templates/templates.go
type VarItem (line 25) | type VarItem struct
type VarItemReflect (line 31) | type VarItemReflect struct
type CTemplateConfig (line 37) | type CTemplateConfig struct
type CTemplateSet (line 49) | type CTemplateSet struct
method SetConfig (line 138) | func (c *CTemplateSet) SetConfig(config CTemplateConfig) {
method GetConfig (line 145) | func (c *CTemplateSet) GetConfig() CTemplateConfig {
method SetBaseImportMap (line 149) | func (c *CTemplateSet) SetBaseImportMap(importMap map[string]string) {
method SetBuildTags (line 153) | func (c *CTemplateSet) SetBuildTags(tags string) {
method SetOverrideTrack (line 157) | func (c *CTemplateSet) SetOverrideTrack(overriden map[string]map[strin...
method GetOverridenRoots (line 161) | func (c *CTemplateSet) GetOverridenRoots() map[string]map[string]bool {
method SetThemeName (line 165) | func (c *CTemplateSet) SetThemeName(name string) {
method SetPerThemeTmpls (line 169) | func (c *CTemplateSet) SetPerThemeTmpls(perThemeTmpls map[string]bool) {
method ResetLogs (line 173) | func (c *CTemplateSet) ResetLogs(in string) {
method buildImportList (line 198) | func (c *CTemplateSet) buildImportList() (importList string) {
method CompileByLoggedin (line 225) | func (c *CTemplateSet) CompileByLoggedin(name, fileDir, expects string...
method Compile (line 316) | func (c *CTemplateSet) Compile(name, fileDir, expects string, expectsI...
method compile (line 330) | func (c *CTemplateSet) compile(name, content, expects string, expectsI...
method rootIterate (line 806) | func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) {
method compileSwitch (line 829) | func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) {
method addText (line 934) | func (c *CTemplateSet) addText(con CContext, text []byte) {
method compileRangeNode (line 949) | func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.Rang...
method skipStructPointers (line 1027) | func (c *CTemplateSet) skipStructPointers(cur reflect.Value, id string...
method checkIfValid (line 1040) | func (c *CTemplateSet) checkIfValid(cur reflect.Value, varHolder strin...
method compileSubSwitch (line 1056) | func (c *CTemplateSet) compileSubSwitch(con CContext, node *parse.Comm...
method compileExprSwitch (line 1168) | func (c *CTemplateSet) compileExprSwitch(con CContext, node *parse.Com...
method unknownNode (line 1207) | func (c *CTemplateSet) unknownNode(n parse.Node) {
method compileIdentSwitchN (line 1214) | func (c *CTemplateSet) compileIdentSwitchN(con CContext, n *parse.Comm...
method dumpSymbol (line 1220) | func (c *CTemplateSet) dumpSymbol(pos int, n *parse.CommandNode, symbo...
method compareFunc (line 1226) | func (c *CTemplateSet) compareFunc(con CContext, pos int, n *parse.Com...
method simpleMath (line 1231) | func (c *CTemplateSet) simpleMath(con CContext, pos int, n *parse.Comm...
method compareJoin (line 1247) | func (c *CTemplateSet) compareJoin(con CContext, pos int, node *parse....
method compileIdentSwitch (line 1279) | func (c *CTemplateSet) compileIdentSwitch(con CContext, node *parse.Co...
method compileReflectSwitch (line 1605) | func (c *CTemplateSet) compileReflectSwitch(con CContext, node *parse....
method compileIfVarSubN (line 1631) | func (c *CTemplateSet) compileIfVarSubN(con CContext, varname string) ...
method compileIfVarSub (line 1637) | func (c *CTemplateSet) compileIfVarSub(con CContext, varname string) (...
method compileBoolSub (line 1727) | func (c *CTemplateSet) compileBoolSub(con CContext, varname string) st...
method debugParam (line 1753) | func (c *CTemplateSet) debugParam(param interface{}, depth int) (pstr ...
method dumpCall (line 1793) | func (c *CTemplateSet) dumpCall(name string, params ...interface{}) {
method retCall (line 1803) | func (c *CTemplateSet) retCall(name string, params ...interface{}) {
method compileVarSub (line 1828) | func (c *CTemplateSet) compileVarSub(con CContext, varname string, val...
method compileSubTemplate (line 1967) | func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.T...
method loadTemplate (line 2136) | func (c *CTemplateSet) loadTemplate(fileDir, name string) (content str...
method afterTemplate (line 2169) | func (c *CTemplateSet) afterTemplate(con CContext, startIndex int /*, ...
method afterTemplateV2 (line 2284) | func (c *CTemplateSet) afterTemplateV2(con CContext, startIndex int /*...
method detail (line 2393) | func (c *CTemplateSet) detail(args ...interface{}) {
method detailf (line 2399) | func (c *CTemplateSet) detailf(left string, args ...interface{}) {
method error (line 2405) | func (c *CTemplateSet) error(args ...interface{}) {
method critical (line 2411) | func (c *CTemplateSet) critical(args ...interface{}) {
function NewCTemplateSet (line 85) | func NewCTemplateSet(in string, logDir ...string) *CTemplateSet {
type SkipBlock (line 182) | type SkipBlock struct
type Skipper (line 187) | type Skipper struct
type OutFrag (line 192) | type OutFrag struct
function inSlice (line 820) | func inSlice(haystack []string, expr string) bool {
function buildUserExprs (line 1814) | func buildUserExprs(holder string) ([]string, []string) {
constant ATTmpl (line 2279) | ATTmpl = iota
constant ATLoop (line 2280) | ATLoop
constant ATIfPtr (line 2281) | ATIfPtr
FILE: common/thaw.go
type ThawInt (line 9) | type ThawInt interface
type SingleServerThaw (line 16) | type SingleServerThaw struct
method Thawed (line 28) | func (t *SingleServerThaw) Thawed() bool {
method Thaw (line 35) | func (t *SingleServerThaw) Thaw() {
function NewSingleServerThaw (line 20) | func NewSingleServerThaw() *SingleServerThaw {
type TestThaw (line 41) | type TestThaw struct
method Thawed (line 47) | func (t *TestThaw) Thawed() bool {
method Thaw (line 50) | func (t *TestThaw) Thaw() {
method Tick (line 52) | func (t *TestThaw) Tick() error {
function NewTestThaw (line 44) | func NewTestThaw() *TestThaw {
type DefaultThaw (line 56) | type DefaultThaw struct
method Tick (line 67) | func (t *DefaultThaw) Tick() error {
method Thawed (line 75) | func (t *DefaultThaw) Thawed() bool {
method Thaw (line 79) | func (t *DefaultThaw) Thaw() {
function NewDefaultThaw (line 60) | func NewDefaultThaw() *DefaultThaw {
FILE: common/theme.go
type Theme (line 30) | type Theme struct
method LoadStaticFiles (line 108) | func (t *Theme) LoadStaticFiles() error {
method AddThemeStaticFiles (line 198) | func (t *Theme) AddThemeStaticFiles() error {
method MapTemplates (line 297) | func (t *Theme) MapTemplates() {
method setActive (line 338) | func (t *Theme) setActive(active bool) error {
method HasDock (line 387) | func (t Theme) HasDock(name string) bool {
method BuildDock (line 396) | func (t Theme) BuildDock(dock string) (sbody string) {
method HasDockByID (line 404) | func (t Theme) HasDockByID(id int) bool {
method BuildDockByID (line 413) | func (t Theme) BuildDockByID(id int) (sbody string) {
method RunTmpl (line 433) | func (t *Theme) RunTmpl(template string, pi interface{}, w io.Writer) ...
method GetTmpl (line 478) | func (t *Theme) GetTmpl(template string) interface{} {
type ThemeSetting (line 70) | type ThemeSetting struct
type TemplateMapping (line 75) | type TemplateMapping struct
constant ResTypeUnknown (line 82) | ResTypeUnknown = iota
constant ResTypeSheet (line 83) | ResTypeSheet
constant ResTypeScript (line 84) | ResTypeScript
constant LocUnknown (line 87) | LocUnknown = iota
constant LocGlobal (line 88) | LocGlobal
constant LocFront (line 89) | LocFront
constant LocPanel (line 90) | LocPanel
type ThemeResource (line 93) | type ThemeResource struct
type ThemeMapTmplToDock (line 102) | type ThemeMapTmplToDock struct
function UpdateDefaultTheme (line 361) | func UpdateDefaultTheme(t *Theme) error {
type GzipResponseWriter (line 421) | type GzipResponseWriter struct
method Write (line 426) | func (w GzipResponseWriter) Write(b []byte) (int, error) {
FILE: common/theme_list.go
type ThemeList (line 22) | type ThemeList
method LoadActiveStatus (line 221) | func (t ThemeList) LoadActiveStatus() error {
method LoadStaticFiles (line 260) | func (t ThemeList) LoadStaticFiles() error {
type ThemeStmts (line 34) | type ThemeStmts struct
function init (line 43) | func init() {
function NewThemeList (line 56) | func NewThemeList() (themes ThemeList, err error) {
function ResetTemplateOverrides (line 269) | func ResetTemplateOverrides() {
function CreateThemeTemplate (line 305) | func CreateThemeTemplate(theme, name string) {
function GetDefaultThemeName (line 315) | func GetDefaultThemeName() string {
function SetDefaultThemeName (line 319) | func SetDefaultThemeName(name string) {
FILE: common/thumbnailer.go
function ThumbTask (line 17) | func ThumbTask(thumbChan chan bool) {
type ThumbnailerInt (line 93) | type ThumbnailerInt interface
type RezThumbnailer (line 97) | type RezThumbnailer struct
method Resize (line 100) | func (thumb *RezThumbnailer) Resize(format, inPath, tmpPath, outPath s...
method resize (line 105) | func (thumb *RezThumbnailer) resize(format, inPath, outPath string, wi...
type CaireThumbnailer (line 110) | type CaireThumbnailer struct
method Resize (line 147) | func (thumb *CaireThumbnailer) Resize(format, inPath, tmpPath, outPath...
function NewCaireThumbnailer (line 113) | func NewCaireThumbnailer() *CaireThumbnailer {
function precodeImage (line 117) | func precodeImage(format, inPath, tmpPath string) error {
FILE: common/tickloop.go
type TickLoop (line 18) | type TickLoop struct
method Loop (line 44) | func (l *TickLoop) Loop() {
function NewTickLoop (line 32) | func NewTickLoop() *TickLoop {
function DBTimeout (line 70) | func DBTimeout() time.Duration {
function StartTick (line 80) | func StartTick() (abort bool) {
function asmMatches (line 102) | func asmMatches() error {
function RunTasks (line 113) | func RunTasks(tasks []func() error) error {
function StartupTasks (line 131) | func StartupTasks() (e error) {
function Dailies (line 158) | func Dailies() (e error) {
type TickWatch (line 232) | type TickWatch struct
method DumpElapsed (line 253) | func (w *TickWatch) DumpElapsed() {
method Run (line 301) | func (w *TickWatch) Run() {
method Stop (line 351) | func (w *TickWatch) Stop() {
method Set (line 355) | func (w *TickWatch) Set(a *int64, v int64) {
method Clear (line 359) | func (w *TickWatch) Clear() {
function NewTickWatch (line 246) | func NewTickWatch() *TickWatch {
FILE: common/topic.go
type Topic (line 29) | type Topic struct
method TopicsRow (line 154) | func (t *Topic) TopicsRow() *TopicsRow {
method cacheRemove (line 252) | func (t *Topic) cacheRemove() {
method AddReply (line 260) | func (t *Topic) AddReply(rid, uid int) (e error) {
method Lock (line 270) | func (t *Topic) Lock() (e error) {
method Unlock (line 276) | func (t *Topic) Unlock() (e error) {
method MoveTo (line 282) | func (t *Topic) MoveTo(destForum int) (e error) {
method TestSetCreatedAt (line 295) | func (t *Topic) TestSetCreatedAt(s time.Time) (e error) {
method Stick (line 302) | func (t *Topic) Stick() (e error) {
method Unstick (line 308) | func (t *Topic) Unstick() (e error) {
method Like (line 316) | func (t *Topic) Like(score, uid int) (err error) {
method Unlike (line 338) | func (t *Topic) Unlike(uid int) error {
method Delete (line 428) | func (t *Topic) Delete() error {
method Update (line 501) | func (t *Topic) Update(name, content string) error {
method SetPoll (line 518) | func (t *Topic) SetPoll(pollID int) error {
method RemovePoll (line 524) | func (t *Topic) RemovePoll() error {
method CreateActionReply (line 532) | func (t *Topic) CreateActionReply(action, ip string, uid int) (err err...
method Author (line 1091) | func (t *Topic) Author() (*User, error) {
method GetID (line 1095) | func (t *Topic) GetID() int {
method GetTable (line 1098) | func (t *Topic) GetTable() string {
method Copy (line 1103) | func (t *Topic) Copy() Topic {
type TopicUser (line 56) | type TopicUser struct
method Replies (line 739) | func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (r...
type TopicsRowMut (line 98) | type TopicsRowMut struct
type TopicsRow (line 104) | type TopicsRow struct
method WebSockets (line 143) | func (r *TopicsRow) WebSockets() *WsTopicsRow {
method WebSockets2 (line 148) | func (r *TopicsRow) WebSockets2(canMod bool) *WsTopicsRow {
type WsTopicsRow (line 117) | type WsTopicsRow struct
type TopicStmts (line 181) | type TopicStmts struct
function init (line 212) | func init() {
function handleLikedTopicReplies (line 352) | func handleLikedTopicReplies(tid int) error {
function handleTopicAttachments (line 375) | func handleTopicAttachments(tid int) error {
function handleReplyAttachments (line 383) | func handleReplyAttachments(rid int) error {
function handleAttachments (line 387) | func handleAttachments(stmt *sql.Stmt, id int) error {
function handleTopicReplies (line 411) | func handleTopicReplies(umap map[int]struct{}, uid, tid int) error {
function GetRidsForTopic (line 555) | func GetRidsForTopic(tid, offset int) (rids []int, e error) {
method Init (line 577) | func (ru *ReplyUser) Init(u *User) (group *Group, err error) {
method Init2 (line 631) | func (ru *ReplyUser) Init2() (group *Group, err error) {
method Init3 (line 677) | func (ru *ReplyUser) Init3(u *User, tu *TopicUser) (group *Group, err er...
function TopicByReplyID (line 1108) | func TopicByReplyID(rid int) (*Topic, error) {
function GetTopicUser (line 1117) | func GetTopicUser(user *User, tid int) (tu TopicUser, err error) {
function copyTopicToTopicUser (line 1163) | func copyTopicToTopicUser(t *Topic, u *User) (tu TopicUser) {
function BlankTopic (line 1197) | func BlankTopic() *Topic {
function BuildTopicURL (line 1201) | func BuildTopicURL(slug string, tid int) string {
function BuildTopicURLSb (line 1208) | func BuildTopicURLSb(sb *strings.Builder, slug string, tid int) {
function getTopicURLPrefix (line 1224) | func getTopicURLPrefix() string {
FILE: common/topic_cache.go
type TopicCache (line 9) | type TopicCache interface
type MemoryTopicCache (line 26) | type MemoryTopicCache struct
method Get (line 43) | func (s *MemoryTopicCache) Get(id int) (*Topic, error) {
method GetUnsafe (line 54) | func (s *MemoryTopicCache) GetUnsafe(id int) (*Topic, error) {
method BulkGet (line 63) | func (s *MemoryTopicCache) BulkGet(ids []int) (list []*Topic) {
method Set (line 74) | func (s *MemoryTopicCache) Set(it *Topic) error {
method Add (line 92) | func (s *MemoryTopicCache) Add(item *Topic) error {
method AddUnsafe (line 105) | func (s *MemoryTopicCache) AddUnsafe(item *Topic) error {
method Remove (line 115) | func (s *MemoryTopicCache) Remove(id int) error {
method RemoveMany (line 128) | func (s *MemoryTopicCache) RemoveMany(ids []int) error {
method RemoveUnsafe (line 144) | func (s *MemoryTopicCache) RemoveUnsafe(id int) error {
method Flush (line 154) | func (s *MemoryTopicCache) Flush() {
method Length (line 163) | func (s *MemoryTopicCache) Length() int {
method SetCapacity (line 168) | func (s *MemoryTopicCache) SetCapacity(cap int) {
method GetCapacity (line 174) | func (s *MemoryTopicCache) GetCapacity() int {
function NewMemoryTopicCache (line 35) | func NewMemoryTopicCache(cap int) *MemoryTopicCache {
FILE: common/topic_list.go
constant TopicListDefault (line 16) | TopicListDefault = iota
constant TopicListMostViewed (line 17) | TopicListMostViewed
constant TopicListWeekViews (line 18) | TopicListWeekViews
type TopicListHolder (line 21) | type TopicListHolder struct
type ForumTopicListHolder (line 27) | type ForumTopicListHolder struct
type TopicListInt (line 33) | type TopicListInt interface
type TopicListIntTest (line 40) | type TopicListIntTest interface
type DefaultTopicList (line 45) | type DefaultTopicList struct
method Tick (line 92) | func (tList *DefaultTopicList) Tick() error {
method defaultPagi (line 288) | func (tList *DefaultTopicList) defaultPagi() Paginator {
method setForumList (line 295) | func (tList *DefaultTopicList) setForumList(forums map[int]*ForumTopic...
method GetListByForum (line 338) | func (tList *DefaultTopicList) GetListByForum(f *Forum, page, orderby ...
method RawGetListByForum (line 358) | func (tList *DefaultTopicList) RawGetListByForum(f *Forum, page, order...
method GetListByGroup (line 419) | func (tList *DefaultTopicList) GetListByGroup(g *Group, page, orderby ...
method GetListByCanSee (line 447) | func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page, ord...
method GetList (line 501) | func (tList *DefaultTopicList) GetList(page, orderby int, filterIDs []...
method getList (line 559) | func (tList *DefaultTopicList) getList(page, orderby, topicCount int, ...
function NewDefaultTopicList (line 70) | func NewDefaultTopicList(acc *qgen.Accumulator) (*DefaultTopicList, erro...
function ForumListToArgQ (line 742) | func ForumListToArgQ(forums []Forum) (argList []interface{}, qlist strin...
function ArgQToTopicCount (line 755) | func ArgQToTopicCount(argList []interface{}, qlist string) (topicCount i...
function ArgQToWeekViewTopicCount (line 770) | func ArgQToWeekViewTopicCount(argList []interface{}, qlist string) (topi...
function TopicCountInForums (line 784) | func TopicCountInForums(forums []Forum) (topicCount int, err error) {
FILE: common/topic_store.go
type TopicStore (line 26) | type TopicStore interface
type DefaultTopicStore (line 50) | type DefaultTopicStore struct
method DirtyGet (line 85) | func (s *DefaultTopicStore) DirtyGet(id int) *Topic {
method Get (line 99) | func (s *DefaultTopicStore) Get(id int) (t *Topic, e error) {
method BypassGet (line 112) | func (s *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
method BulkGetMap (line 131) | func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic...
method Reload (line 204) | func (s *DefaultTopicStore) Reload(id int) error {
method Exists (line 215) | func (s *DefaultTopicStore) Exists(id int) bool {
method ClearIPs (line 219) | func (s *DefaultTopicStore) ClearIPs() error {
method LockMany (line 224) | func (s *DefaultTopicStore) LockMany(tids []int) (e error) {
method Create (line 264) | func (s *DefaultTopicStore) Create(fid int, name, content string, uid ...
method AddLastTopic (line 297) | func (s *DefaultTopicStore) AddLastTopic(t *Topic, fid int) error {
method Count (line 303) | func (s *DefaultTopicStore) Count() (count int) {
method CountUser (line 306) | func (s *DefaultTopicStore) CountUser(uid int) (count int) {
method CountMegaUser (line 309) | func (s *DefaultTopicStore) CountMegaUser(uid int) (count int) {
method CountBigUser (line 312) | func (s *DefaultTopicStore) CountBigUser(uid int) (count int) {
method SetCache (line 316) | func (s *DefaultTopicStore) SetCache(cache TopicCache) {
method GetCache (line 321) | func (s *DefaultTopicStore) GetCache() TopicCache {
function NewDefaultTopicStore (line 65) | func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
FILE: common/user.go
type User (line 30) | type User struct
method WebSockets (line 74) | func (u *User) WebSockets() *WsJSONUser {
method Me (line 97) | func (u *User) Me() *MeUser {
method Init (line 222) | func (u *User) Init() {
method CacheRemove (line 234) | func (u *User) CacheRemove() {
method Ban (line 241) | func (u *User) Ban(dur time.Duration, issuedBy int) error {
method Unban (line 245) | func (u *User) Unban() error {
method deleteScheduleGroupTx (line 249) | func (u *User) deleteScheduleGroupTx(tx *sql.Tx) error {
method setTempGroupTx (line 258) | func (u *User) setTempGroupTx(tx *sql.Tx, tempGroup int) error {
method ScheduleGroupUpdate (line 268) | func (u *User) ScheduleGroupUpdate(gid, issuedBy int, dur time.Duratio...
method RevertGroupUpdate (line 305) | func (u *User) RevertGroupUpdate() error {
method Activate (line 329) | func (u *User) Activate() (e error) {
method Delete (line 342) | func (u *User) Delete() error {
method DeletePosts (line 349) | func (u *User) DeletePosts() error {
method bindStmt (line 517) | func (u *User) bindStmt(stmt *sql.Stmt, params ...interface{}) (e erro...
method ChangeName (line 524) | func (u *User) ChangeName(name string) error {
method ChangeAvatar (line 528) | func (u *User) ChangeAvatar(avatar string) error {
method ScheduleAvatarResize (line 533) | func (u *User) ScheduleAvatarResize() (e error) {
method ChangeGroup (line 549) | func (u *User) ChangeGroup(group int) error {
method GetIP (line 553) | func (u *User) GetIP() string {
method UpdateIP (line 559) | func (u *User) UpdateIP(ip string) error {
method UpdatePrivacy (line 575) | func (u *User) UpdatePrivacy(profileComments, enableEmbeds int) error {
method Update (line 589) | func (u *User) Update(name, email string, group int) (err error) {
method IncreasePostStats (line 593) | func (u *User) IncreasePostStats(wcount int, topic bool) (err error) {
method countf (line 625) | func (u *User) countf(stmt *sql.Stmt) (count int) {
method RecalcPostStats (line 633) | func (u *User) RecalcPostStats() error {
method DecreasePostStats (line 661) | func (u *User) DecreasePostStats(wcount int, topic bool) (err error) {
method ResetPostStats (line 687) | func (u *User) ResetPostStats() error {
method Copy (line 694) | func (u *User) Copy() User {
method InitPerms (line 699) | func (u *User) InitPerms() {
type UserPrivacy (line 68) | type UserPrivacy struct
type WsJSONUser (line 84) | type WsJSONUser struct
type MeUser (line 106) | type MeUser struct
type UserStmts (line 130) | type UserStmts struct
function init (line 171) | func init() {
function InitPerms2 (line 726) | func InitPerms2(group int, superAdmin bool, tempGroup int) (perms *Perms...
function PrivacyAllowMessage (line 750) | func PrivacyAllowMessage(pu, u *User) (canMsg bool) {
function PrivacyCommentsShow (line 766) | func PrivacyCommentsShow(pu, u *User) (showComments bool) {
type GuestAvatar (line 786) | type GuestAvatar struct
function buildNoavatar (line 791) | func buildNoavatar(uid, width int) string {
function BuildAvatar (line 826) | func BuildAvatar(uid int, avatar string) (normalAvatar, microAvatar stri...
function SetPassword (line 845) | func SetPassword(uid int, password string) error {
function wordsToScore (line 855) | func wordsToScore(wcount int, topic bool) (score int) {
function BlankUser (line 871) | func BlankUser() *User {
function BuildProfileURL (line 876) | func BuildProfileURL(slug string, uid int) string {
function BuildProfileURLSb (line 883) | func BuildProfileURLSb(sb *strings.Builder, slug string, uid int) {
FILE: common/user_cache.go
type UserCache (line 9) | type UserCache interface
type MemoryUserCache (line 27) | type MemoryUserCache struct
method DeallocOverflow (line 44) | func (s *MemoryUserCache) DeallocOverflow(evictPriority bool) (evicted...
method Get (line 97) | func (s *MemoryUserCache) Get(id int) (*User, error) {
method Getn (line 107) | func (s *MemoryUserCache) Getn(id int) *User {
method BulkGet (line 115) | func (s *MemoryUserCache) BulkGet(ids []int) (list []*User) {
method GetUnsafe (line 126) | func (s *MemoryUserCache) GetUnsafe(id int) (*User, error) {
method Set (line 135) | func (s *MemoryUserCache) Set(item *User) error {
method Add (line 154) | func (s *MemoryUserCache) Add(item *User) error {
method AddUnsafe (line 167) | func (s *MemoryUserCache) AddUnsafe(item *User) error {
method Remove (line 177) | func (s *MemoryUserCache) Remove(id int) error {
method RemoveUnsafe (line 191) | func (s *MemoryUserCache) RemoveUnsafe(id int) error {
method BulkRemove (line 201) | func (s *MemoryUserCache) BulkRemove(ids []int) {
method Flush (line 216) | func (s *MemoryUserCache) Flush() {
method Length (line 225) | func (s *MemoryUserCache) Length() int {
method SetCapacity (line 230) | func (s *MemoryUserCache) SetCapacity(cap int) {
method GetCapacity (line 236) | func (s *MemoryUserCache) GetCapacity() int {
function NewMemoryUserCache (line 36) | func NewMemoryUserCache(cap int) *MemoryUserCache {
FILE: common/user_store.go
type UserStore (line 19) | type UserStore interface
type DefaultUserStore (line 43) | type DefaultUserStore struct
method DirtyGet (line 90) | func (s *DefaultUserStore) DirtyGet(id int) *User {
method scanUser (line 101) | func (s *DefaultUserStore) scanUser(r *sql.Row, u *User) (embeds int, ...
method Get (line 107) | func (s *DefaultUserStore) Get(id int) (*User, error) {
method Getn (line 130) | func (s *DefaultUserStore) Getn(id int) *User {
method GetByName (line 152) | func (s *DefaultUserStore) GetByName(name string) (*User, error) {
method BulkGetByName (line 170) | func (s *DefaultUserStore) BulkGetByName(names []string) (list []*User...
method RawBulkGetByNameForConvo (line 215) | func (s *DefaultUserStore) RawBulkGetByNameForConvo(f func(int, string...
method GetOffset (line 238) | func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*Us...
method SearchOffset (line 262) | func (s *DefaultUserStore) SearchOffset(name, email string, gid, offse...
method Each (line 286) | func (s *DefaultUserStore) Each(f func(*User) error) error {
method BulkGetMap (line 312) | func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, ...
method BypassGet (line 388) | func (s *DefaultUserStore) BypassGet(id int) (*User, error) {
method Reload (line 401) | func (s *DefaultUserStore) Reload(id int) error {
method Exists (line 412) | func (s *DefaultUserStore) Exists(id int) bool {
method ClearLastIPs (line 420) | func (s *DefaultUserStore) ClearLastIPs() error {
method Create (line 427) | func (s *DefaultUserStore) Create(name, password, email string, group ...
method Count (line 458) | func (s *DefaultUserStore) Count() (count int) {
method CountSearch (line 462) | func (s *DefaultUserStore) CountSearch(name, email string, gid int) (c...
method SetCache (line 466) | func (s *DefaultUserStore) SetCache(cache UserCache) {
method GetCache (line 471) | func (s *DefaultUserStore) GetCache() UserCache {
function NewDefaultUserStore (line 62) | func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
FILE: common/utils.go
type Version (line 27) | type Version struct
method String (line 36) | func (ver *Version) String() (out string) {
function GenerateSafeString (line 49) | func GenerateSafeString(len int) (string, error) {
function GenerateStd32SafeString (line 60) | func GenerateStd32SafeString(len int) (string, error) {
function RelativeTimeFromString (line 70) | func RelativeTimeFromString(in string) (string, error) {
function RelativeTime (line 84) | func RelativeTime(t time.Time) string {
function ConvertPerfUnit (line 172) | func ConvertPerfUnit(quan float64) (out float64, unit string) {
function ConvertByteUnit (line 193) | func ConvertByteUnit(bytes float64) (float64, string) {
function ConvertByteInUnit (line 210) | func ConvertByteInUnit(bytes float64, unit string) (count float64) {
function FriendlyUnitToBytes (line 234) | func FriendlyUnitToBytes(quantity int, unit string) (bytes int, err erro...
function ConvertUnit (line 256) | func ConvertUnit(num int) (int, string) {
function ConvertFriendlyUnit (line 273) | func ConvertFriendlyUnit(num int) (int, string) {
function NameToSlug (line 317) | func NameToSlug(name string) (slug string) {
function HasSuspiciousEmail (line 343) | func HasSuspiciousEmail(email string) bool {
function unmarshalJsonFile (line 369) | func unmarshalJsonFile(name string, in interface{}) error {
function unmarshalJsonFileIgnore404 (line 377) | func unmarshalJsonFileIgnore404(name string, in interface{}) error {
function CanonEmail (line 387) | func CanonEmail(email string) string {
function createFile (line 400) | func createFile(name string) error {
function writeFile (line 409) | func writeFile(name, content string) (err error) {
function Stripslashes (line 426) | func Stripslashes(text string) string {
function WordCount (line 432) | func WordCount(input string) (count int) {
function GetLevel (line 454) | func GetLevel(score int) (level int) {
function GetLevelScore (line 475) | func GetLevelScore(getLevel int) (score int) {
function GetLevels (line 493) | func GetLevels(maxLevel int) []float64 {
function SanitiseSingleLine (line 514) | func SanitiseSingleLine(in string) string {
function SanitiseBody (line 524) | func SanitiseBody(in string) string {
function BuildSlug (line 530) | func BuildSlug(slug string, id int) string {
FILE: common/weak_passwords.go
type weakpassHolder (line 23) | type weakpassHolder struct
function InitWeakPasswords (line 28) | func InitWeakPasswords() error {
function WeakPassword (line 78) | func WeakPassword(password, username, email string) error {
FILE: common/websockets.go
function init (line 34) | func init() {
type WsTopicList (line 41) | type WsTopicList struct
function RouteWebsockets (line 49) | func RouteWebsockets(w http.ResponseWriter, r *http.Request, user *User)...
function ParseSEOURL (line 123) | func ParseSEOURL(urlBit string) (slug string, id int, err error) {
function wsPageResponses (line 133) | func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
function wsPageResume (line 216) | func wsPageResume(wsUser *WSUser, conn *websocket.Conn, page string, res...
function wsLeavePage (line 235) | func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
function adminStatsTicker (line 292) | func adminStatsTicker() {
FILE: common/widget.go
type WidgetStmts (line 13) | type WidgetStmts struct
function init (line 25) | func init() {
type Widget (line 41) | type Widget struct
method Delete (line 59) | func (w *Widget) Delete() error {
method Copy (line 76) | func (w *Widget) Copy() (ow *Widget) {
method Allowed (line 85) | func (w *Widget) Allowed(zone string, zoneid int) bool {
method Build (line 110) | func (w *Widget) Build(hvars interface{}) (string, error) {
type WidgetEdit (line 122) | type WidgetEdit struct
method Create (line 127) | func (w *WidgetEdit) Create() (int, error) {
method Commit (line 148) | func (w *WidgetEdit) Commit() error {
FILE: common/widget_search_and_filter.go
type filterForum (line 6) | type filterForum struct
type searchAndFilter (line 10) | type searchAndFilter struct
function widgetSearchAndFilter (line 15) | func widgetSearchAndFilter(widget *Widget, hvars interface{}) (out strin...
FILE: common/widget_store.go
type DefaultWidgetStore (line 10) | type DefaultWidgetStore struct
method Get (line 19) | func (s *DefaultWidgetStore) Get(id int) (*Widget, error) {
method set (line 29) | func (s *DefaultWidgetStore) set(w *Widget) {
method delete (line 35) | func (s *DefaultWidgetStore) delete(id int) {
function NewDefaultWidgetStore (line 15) | func NewDefaultWidgetStore() *DefaultWidgetStore {
FILE: common/widget_wol.go
type wolUsers (line 12) | type wolUsers struct
function wolInit (line 19) | func wolInit(w *Widget, sched *WidgetScheduler) error {
function wolGetUsers (line 24) | func wolGetUsers() ([]*User, int) {
function wolBuild (line 37) | func wolBuild(w *Widget, hvars interface{}) (string, error) {
function wolRender (line 44) | func wolRender(w *Widget, hvars interface{}) (string, error) {
function wolTick (line 57) | func wolTick(widget *Widget) error {
FILE: common/widget_wol_context.go
function wolContextRender (line 5) | func wolContextRender(widget *Widget, hvars interface{}) (string, error) {
FILE: common/widgets.go
type WidgetDock (line 22) | type WidgetDock struct
type WidgetDocks (line 27) | type WidgetDocks struct
type WidgetMenu (line 36) | type WidgetMenu struct
type WidgetMenuItem (line 41) | type WidgetMenuItem struct
type NameTextPair (line 47) | type NameTextPair struct
function preparseWidget (line 52) | func preparseWidget(w *Widget, wdata string) (e error) {
function GetDockList (line 114) | func GetDockList() []string {
function GetDock (line 123) | func GetDock(dock string) []*Widget {
function HasDock (line 137) | func HasDock(dock string) bool {
function HasWidgets (line 146) | func HasWidgets(dock string, h *Header) bool {
function BuildWidget (line 181) | func BuildWidget(dock string, h *Header) (sbody string) {
function BuildWidget2 (line 236) | func BuildWidget2(dock int, h *Header) (sbody string) {
function BuildWidget3 (line 283) | func BuildWidget3(dock int, h *Header) {
function HasWidgets2 (line 332) | func HasWidgets2(dock int, h *Header) bool {
function getDockWidgets (line 368) | func getDockWidgets(dock string) (widgets []*Widget, e error) {
function InitWidgets (line 392) | func InitWidgets() (fi error) {
function releaseWidgets (line 422) | func releaseWidgets(ws []*Widget) {
function setDock (line 431) | func setDock(dock string, widgets []*Widget) {
type WidgetScheduler (line 470) | type WidgetScheduler struct
method Add (line 475) | func (s *WidgetScheduler) Add(w *Widget) {
method Store (line 479) | func (s *WidgetScheduler) Store() {
method Tick (line 483) | func (s *WidgetScheduler) Tick() error {
FILE: common/word_filters.go
type WordFilter (line 11) | type WordFilter struct
type WordFilterDiff (line 16) | type WordFilterDiff struct
type WordFilterStore (line 25) | type WordFilterStore interface
type DefaultWordFilterStore (line 37) | type DefaultWordFilterStore struct
method ReloadAll (line 66) | func (s *DefaultWordFilterStore) ReloadAll() error {
method bypassGetAll (line 82) | func (s *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter...
method GetAll (line 101) | func (s *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter...
method Get (line 105) | func (s *DefaultWordFilterStore) Get(id int) (*WordFilter, error) {
method Create (line 112) | func (s *DefaultWordFilterStore) Create(find, replace string) (int, er...
method Delete (line 125) | func (s *DefaultWordFilterStore) Delete(id int) error {
method Update (line 133) | func (s *DefaultWordFilterStore) Update(id int, find, replace string) ...
method Length (line 142) | func (s *DefaultWordFilterStore) Length() int {
method EstCount (line 147) | func (s *DefaultWordFilterStore) EstCount() int {
method Count (line 152) | func (s *DefaultWordFilterStore) Count() (count int) {
function NewDefaultWordFilterStore (line 48) | func NewDefaultWordFilterStore(acc *qgen.Accumulator) (*DefaultWordFilte...
FILE: common/ws_hub.go
type WsHubImpl (line 17) | type WsHubImpl struct
method Start (line 41) | func (h *WsHubImpl) Start() {
method Tick (line 74) | func (h *WsHubImpl) Tick() error {
method GuestCount (line 297) | func (h *WsHubImpl) GuestCount() int {
method UserCount (line 303) | func (h *WsHubImpl) UserCount() (count int) {
method HasUser (line 314) | func (h *WsHubImpl) HasUser(uid int) (exists bool) {
method broadcastMessage (line 328) | func (h *WsHubImpl) broadcastMessage(msg string) error {
method getUser (line 348) | func (h *WsHubImpl) getUser(uid int) (wsUser *WSUser, err error) {
method getUsers (line 367) | func (h *WsHubImpl) getUsers(uids []int) (wsUsers []*WSUser, err error) {
method AllUsers (line 392) | func (h *WsHubImpl) AllUsers() (users []*User) {
method removeUser (line 405) | func (h *WsHubImpl) removeUser(uid int) {
method AddConn (line 417) | func (h *WsHubImpl) AddConn(user *User, conn *websocket.Conn) (*WSUser...
method RemoveConn (line 460) | func (h *WsHubImpl) RemoveConn(wsUser *WSUser, conn *websocket.Conn) {
method PushMessage (line 469) | func (h *WsHubImpl) PushMessage(targetUser int, msg string) error {
method pushAlert (line 477) | func (h *WsHubImpl) pushAlert(targetUser int, a Alert) error {
method pushAlerts (line 489) | func (h *WsHubImpl) pushAlerts(users []int, a Alert) error {
function init (line 32) | func init() {
function wsTopicListTick (line 78) | func wsTopicListTick(h *WsHubImpl) error {
FILE: common/ws_user.go
type WSUser (line 14) | type WSUser struct
method Ping (line 25) | func (u *WSUser) Ping() error {
method WriteAll (line 59) | func (u *WSUser) WriteAll(msg string) error {
method WriteToPage (line 75) | func (u *WSUser) WriteToPage(msg, page string) error {
method WriteToPageBytes (line 80) | func (u *WSUser) WriteToPageBytes(msg []byte, page string) error {
method WriteToPageBytesMulti (line 104) | func (u *WSUser) WriteToPageBytesMulti(msgs [][]byte, page string) err...
method CountSockets (line 129) | func (u *WSUser) CountSockets() int {
method AddSocket (line 135) | func (u *WSUser) AddSocket(conn *websocket.Conn, page string) {
method RemoveSocket (line 153) | func (u *WSUser) RemoveSocket(conn *websocket.Conn) {
method SetPageForSocket (line 200) | func (u *WSUser) SetPageForSocket(conn *websocket.Conn, page string) e...
method InPage (line 219) | func (u *WSUser) InPage(page string) bool {
method FinalizePage (line 233) | func (u *WSUser) FinalizePage(page string, h func()) {
type WSUserSocket (line 20) | type WSUserSocket struct
FILE: database.go
function InitDatabase (line 21) | func InitDatabase() (err error) {
FILE: experimental/counterTree/tree.go
constant debug (line 10) | debug = true
type TreeCounterNode (line 12) | type TreeCounterNode struct
type TreeTopicViewCounter (line 20) | type TreeTopicViewCounter struct
method Bump (line 30) | func (counter *TreeTopicViewCounter) Bump(signTopicID int64) {
function newTreeTopicViewCounter (line 24) | func newTreeTopicViewCounter() *TreeTopicViewCounter {
FILE: experimental/counterTree/tree_test.go
function TestCounter (line 8) | func TestCounter(t *testing.T) {
function TestScope (line 17) | func TestScope(t *testing.T) {
FILE: experimental/plugin_geoip.go
function init (line 9) | func init() {
function initGeoip (line 13) | func initGeoip(plugin *c.Plugin) (err error) {
function deactivateGeoip (line 18) | func deactivateGeoip(plugin *c.Plugin) {
FILE: experimental/plugin_sendmail.go
function init (line 16) | func init() {
function initSendmail (line 24) | func initSendmail(plugin *c.Plugin) error {
function activateSendmail (line 30) | func activateSendmail(plugin *c.Plugin) error {
function deactivateSendmail (line 40) | func deactivateSendmail(plugin *c.Plugin) {
function sendSendmail (line 44) | func sendSendmail(data ...interface{}) interface{} {
FILE: extend/adventure/lib/adventure.go
type Adventure (line 4) | type Adventure struct
method GetTable (line 13) | func (a *Adventure) GetTable() string {
FILE: extend/adventure/lib/adventure_store.go
type AdventureStore (line 3) | type AdventureStore interface
type DefaultAdventureStore (line 7) | type DefaultAdventureStore struct
FILE: extend/guilds/lib/guild_store.go
type GuildStore (line 11) | type GuildStore interface
type SQLGuildStore (line 16) | type SQLGuildStore struct
method Close (line 29) | func (s *SQLGuildStore) Close() {
method Get (line 34) | func (s *SQLGuildStore) Get(id int) (g *Guild, err error) {
method Create (line 40) | func (s *SQLGuildStore) Create(name, desc string, active bool, privacy...
function NewSQLGuildStore (line 21) | func NewSQLGuildStore() (*SQLGuildStore, error) {
FILE: extend/guilds/lib/guilds.go
type Guild (line 29) | type Guild struct
type Page (line 52) | type Page struct
type ListPage (line 63) | type ListPage struct
type MemberListPage (line 69) | type MemberListPage struct
type Member (line 79) | type Member struct
function PrebuildTmplList (line 90) | func PrebuildTmplList(user *c.User, h *c.Header) c.CTmpl {
function CommonAreaWidgets (line 113) | func CommonAreaWidgets(header *c.Header) {
function GuildWidgets (line 135) | func GuildWidgets(header *c.Header, guildItem *Guild) (success bool) {
function RouteGuildList (line 164) | func RouteGuildList(w http.ResponseWriter, r *http.Request, user *c.User...
function MiddleViewGuild (line 195) | func MiddleViewGuild(w http.ResponseWriter, r *http.Request, user *c.Use...
function RouteCreateGuild (line 218) | func RouteCreateGuild(w http.ResponseWriter, r *http.Request, user *c.Us...
function RouteCreateGuildSubmit (line 233) | func RouteCreateGuildSubmit(w http.ResponseWriter, r *http.Request, user...
function RouteMemberList (line 282) | func RouteMemberList(w http.ResponseWriter, r *http.Request, user *c.Use...
function AttachForum (line 347) | func AttachForum(guildID, fid int) error {
function UnattachForum (line 352) | func UnattachForum(fid int) error {
function BuildGuildURL (line 357) | func BuildGuildURL(slug string, id int) string {
function PreRenderViewForum (line 369) | func PreRenderViewForum(w http.ResponseWriter, r *http.Request, user *c....
function TrowAssign (line 387) | func TrowAssign(args ...interface{}) interface{} {
function TopicCreatePreLoop (line 397) | func TopicCreatePreLoop(args ...interface{}) interface{} {
function ForumCheck (line 409) | func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) {
function Widgets (line 472) | func Widgets(args ...interface{}) interface{} {
FILE: extend/guilds/plugin_guilds.go
function init (line 11) | func init() {
function initGuilds (line 18) | func initGuilds(pl *c.Plugin) (err error) {
function deactivateGuilds (line 57) | func deactivateGuilds(pl *c.Plugin) {
function installGuilds (line 81) | func installGuilds(plugin *c.Plugin) error {
function uninstallGuilds (line 129) | func uninstallGuilds(plugin *c.Plugin) error {
FILE: extend/plugin_adventure.go
function init (line 6) | func init() {
function initAdventure (line 19) | func initAdventure(pl *c.Plugin) error {
function deactivateAdventure (line 24) | func deactivateAdventure(pl *c.Plugin) {
function installAdventure (line 27) | func installAdventure(pl *c.Plugin) error {
FILE: extend/plugin_bbcode.go
function init (line 29) | func init() {
function InitBbcode (line 33) | func InitBbcode(pl *c.Plugin) error {
function deactivateBbcode (line 57) | func deactivateBbcode(pl *c.Plugin) {
function BbcodeStripTags (line 62) | func BbcodeStripTags(msg string) string {
function BbcodeRegexParse (line 70) | func BbcodeRegexParse(msg string) string {
function bbcodeSimpleParse (line 85) | func bbcodeSimpleParse(msg string) string {
function BbcodeParseWithoutCode (line 135) | func BbcodeParseWithoutCode(msg string) string {
function BbcodeFullParse (line 226) | func BbcodeFullParse(msg string) string {
function bbcodeParseURL (line 361) | func bbcodeParseURL(i int, start int, lastTag int, mbytes []byte, outbyt...
function bbcodeParseRand (line 382) | func bbcodeParseRand(i int, start int, lastTag int, msgbytes []byte, out...
FILE: extend/plugin_heythere.go
function init (line 5) | func init() {
function initHeythere (line 10) | func initHeythere(plugin *c.Plugin) error {
function deactivateHeythere (line 15) | func deactivateHeythere(plugin *c.Plugin) {
function heythereReply (line 19) | func heythereReply(data ...interface{}) interface{} {
FILE: extend/plugin_hyperdrive.go
function init (line 21) | func init() {
function initHdrive (line 25) | func initHdrive(pl *c.Plugin) error {
function deactivateHdrive (line 35) | func deactivateHdrive(pl *c.Plugin) {
type Hyperspace (line 43) | type Hyperspace struct
function newHyperspace (line 49) | func newHyperspace() *Hyperspace {
function tickHdriveWol (line 58) | func tickHdriveWol(args ...interface{}) (skip bool, rerr c.RouteError) {
function tickHdrive (line 64) | func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) {
function jumpHdriveTopicList (line 162) | func jumpHdriveTopicList(args ...interface{}) (skip bool, rerr c.RouteEr...
function jumpHdriveForumList (line 168) | func jumpHdriveForumList(args ...interface{}) (skip bool, rerr c.RouteEr...
function jumpHdrive (line 174) | func jumpHdrive( /*pg, */ p [3][]byte, args []interface{}) (skip bool, r...
FILE: extend/plugin_markdown.go
function init (line 27) | func init() {
function InitMarkdown (line 31) | func InitMarkdown(pl *c.Plugin) error {
function deactivateMarkdown (line 53) | func deactivateMarkdown(pl *c.Plugin) {
function MarkdownParse (line 59) | func MarkdownParse(msg string) string {
function _markdownParse (line 68) | func _markdownParse(msg string, n int) string {
function isMarkdownStartChar (line 307) | func isMarkdownStartChar(ch byte) bool { // char
function markdownFindChar (line 311) | func markdownFindChar(data string, index int, char byte) bool {
function markdownSkipUntilChar (line 321) | func markdownSkipUntilChar(data string, index int, char byte) int {
function markdownSkipUntilNotChar (line 330) | func markdownSkipUntilNotChar(data string, index int, char byte) int {
function markdownSkipUntilStrongSpace (line 339) | func markdownSkipUntilStrongSpace(data string, index int) int {
function markdownSkipUntilAsterisk (line 356) | func markdownSkipUntilAsterisk(data string, index int) int {
function markdownSkipList (line 372) | func markdownSkipList(data string, index int) int {
FILE: extend/plugin_skeleton.go
function init (line 5) | func init() {
function initSkeleton (line 34) | func initSkeleton(pl *c.Plugin) error { return nil }
function activateSkeleton (line 37) | func activateSkeleton(pl *c.Plugin) error { return nil }
function deactivateSkeleton (line 39) | func deactivateSkeleton(pl *c.Plugin) {}
FILE: gen_mssql.go
type Stmts (line 11) | type Stmts struct
function _gen_mssql (line 28) | func _gen_mssql() (err error) {
FILE: gen_mysql.go
type Stmts (line 13) | type Stmts struct
function _gen_mysql (line 30) | func _gen_mysql() (err error) {
FILE: gen_pgsql.go
type Stmts (line 11) | type Stmts struct
function _gen_pgsql (line 24) | func _gen_pgsql() (err error) {
FILE: gen_router.go
type HTTPSRedirect (line 900) | type HTTPSRedirect struct
method ServeHTTP (line 902) | func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.R...
method SuspiciousRequest (line 909) | func (r *GenRouter) SuspiciousRequest(req *http.Request, pre string) {
method ServeHTTP (line 926) | func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
method routeSwitch (line 1297) | func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request...
FILE: general_test.go
function ResetTables (line 30) | func ResetTables() (err error) {
function gloinit (line 49) | func gloinit() (e error) {
function rcfg (line 113) | func rcfg() *RouterConfig {
function init (line 120) | func init() {
function BenchmarkTopicAdminRouteParallel (line 132) | func BenchmarkTopicAdminRouteParallel(b *testing.B) {
function BenchmarkTopicAdminRouteParallelWithRouter (line 176) | func BenchmarkTopicAdminRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopicAdminRouteParallelAlt (line 218) | func BenchmarkTopicAdminRouteParallelAlt(b *testing.B) {
function BenchmarkTopicAdminRouteParallelWithRouterAlt (line 222) | func BenchmarkTopicAdminRouteParallelWithRouterAlt(b *testing.B) {
function BenchmarkTopicAdminRouteParallelAltAlt (line 226) | func BenchmarkTopicAdminRouteParallelAltAlt(b *testing.B) {
function BenchmarkTopicGuestAdminRouteParallelWithRouterPre (line 230) | func BenchmarkTopicGuestAdminRouteParallelWithRouterPre(b *testing.B) {
function BenchmarkTopicGuestAdminRouteParallelWithRouter (line 234) | func BenchmarkTopicGuestAdminRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopicGuestAdminRouteParallelWithRouterPre2 (line 289) | func BenchmarkTopicGuestAdminRouteParallelWithRouterPre2(b *testing.B) {
function BenchmarkTopicGuestAdminRouteParallelWithRouterGC (line 293) | func BenchmarkTopicGuestAdminRouteParallelWithRouterGC(b *testing.B) {
function BenchmarkTopicGuestRouteParallel (line 350) | func BenchmarkTopicGuestRouteParallel(b *testing.B) {
function BenchmarkForumsRouteAdminParallelWithRouterGC2Pre (line 379) | func BenchmarkForumsRouteAdminParallelWithRouterGC2Pre(b *testing.B) {
function BenchmarkForumsRouteAdminParallelWithRouterGC2 (line 383) | func BenchmarkForumsRouteAdminParallelWithRouterGC2(b *testing.B) {
function BenchmarkForumsRouteAdminParallelWithRouterGCBrotliPre (line 427) | func BenchmarkForumsRouteAdminParallelWithRouterGCBrotliPre(b *testing.B) {
function BenchmarkForumsRouteAdminParallelWithRouterGCBrotli (line 431) | func BenchmarkForumsRouteAdminParallelWithRouterGCBrotli(b *testing.B) {
function BenchmarkTopicRouteAdminParallelWithRouterGC2Pre (line 479) | func BenchmarkTopicRouteAdminParallelWithRouterGC2Pre(b *testing.B) {
function BenchmarkTopicRouteAdminParallelWithRouterGC2 (line 483) | func BenchmarkTopicRouteAdminParallelWithRouterGC2(b *testing.B) {
function BenchmarkTopicRouteAdminParallelWithRouterGCBrotliPre (line 527) | func BenchmarkTopicRouteAdminParallelWithRouterGCBrotliPre(b *testing.B) {
function BenchmarkTopicRouteAdminParallelWithRouterGCBrotli (line 531) | func BenchmarkTopicRouteAdminParallelWithRouterGCBrotli(b *testing.B) {
function BenchmarkTopicsRouteAdminParallelWithRouterGC2Pre (line 578) | func BenchmarkTopicsRouteAdminParallelWithRouterGC2Pre(b *testing.B) {
function BenchmarkTopicsRouteAdminParallelWithRouterGC2 (line 582) | func BenchmarkTopicsRouteAdminParallelWithRouterGC2(b *testing.B) {
function BenchmarkTopicsRouteAdminParallelWithRouterGCBrotliPre (line 626) | func BenchmarkTopicsRouteAdminParallelWithRouterGCBrotliPre(b *testing.B) {
function BenchmarkTopicsRouteAdminParallelWithRouterGCBrotli (line 630) | func BenchmarkTopicsRouteAdminParallelWithRouterGCBrotli(b *testing.B) {
function BenchmarkTopicsRouteAdminParallelWithRouterGCGzipPre (line 675) | func BenchmarkTopicsRouteAdminParallelWithRouterGCGzipPre(b *testing.B) {
function BenchmarkTopicsRouteAdminParallelWithRouterGCGzip (line 679) | func BenchmarkTopicsRouteAdminParallelWithRouterGCGzip(b *testing.B) {
function BenchmarkTopicGuestRouteParallelDebugMode (line 724) | func BenchmarkTopicGuestRouteParallelDebugMode(b *testing.B) {
function BenchmarkAlertsRouteAdminParallelWithRouterGCPre (line 751) | func BenchmarkAlertsRouteAdminParallelWithRouterGCPre(b *testing.B) {
function BenchmarkAlertsRouteAdminParallelWithRouterGC (line 755) | func BenchmarkAlertsRouteAdminParallelWithRouterGC(b *testing.B) {
function obRoute (line 799) | func obRoute(b *testing.B, path string) {
function obRouteNoError (line 808) | func obRouteNoError(b *testing.B, path string) {
function BenchmarkTopicsGuestRouteParallelWithRouter (line 817) | func BenchmarkTopicsGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopicsGuestJSRouteParallelWithRouter (line 821) | func BenchmarkTopicsGuestJSRouteParallelWithRouter(b *testing.B) {
function BenchmarkForumsGuestRouteParallelWithRouter (line 825) | func BenchmarkForumsGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkForumGuestRouteParallelWithRouter (line 829) | func BenchmarkForumGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopicGuestRouteParallelWithRouter (line 833) | func BenchmarkTopicGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopicGuestRouteParallelWithRouterAlt (line 837) | func BenchmarkTopicGuestRouteParallelWithRouterAlt(b *testing.B) {
function BenchmarkBadRouteGuestRouteParallelWithRouter (line 841) | func BenchmarkBadRouteGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkAlertsRouteGuestParallelWithRouter (line 845) | func BenchmarkAlertsRouteGuestParallelWithRouter(b *testing.B) {
function binit (line 851) | func binit(b *testing.B) {
type StashConfig (line 858) | type StashConfig struct
method Restore (line 869) | func (cfg *StashConfig) Restore() {
function NewStashConfig (line 863) | func NewStashConfig() *StashConfig {
function benchRoute (line 874) | func benchRoute(b *testing.B, path string) func(*testing.PB) {
function benchRouteNoError (line 895) | func benchRouteNoError(b *testing.B, path string) func(*testing.PB) {
function BenchmarkProfileGuestRouteParallelWithRouter (line 912) | func BenchmarkProfileGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkPopulateTopicWithRouter (line 916) | func BenchmarkPopulateTopicWithRouter(b *testing.B) {
function BenchmarkTopicAdminFullPageRouteParallelWithRouter (line 938) | func BenchmarkTopicAdminFullPageRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopicGuestFullPageRouteParallelWithRouter (line 951) | func BenchmarkTopicGuestFullPageRouteParallelWithRouter(b *testing.B) {
function BenchmarkPopulateTopicMentionWithRouter (line 967) | func BenchmarkPopulateTopicMentionWithRouter(b *testing.B) {
function BenchmarkTopicMentionAdminFullPageRouteParallelWithRouter (line 994) | func BenchmarkTopicMentionAdminFullPageRouteParallelWithRouter(b *testin...
function BenchmarkTopicMentionGuestFullPageRouteParallelWithRouter (line 1004) | func BenchmarkTopicMentionGuestFullPageRouteParallelWithRouter(b *testin...
function BenchmarkPopulateTopic10MentionWithRouter (line 1011) | func BenchmarkPopulateTopic10MentionWithRouter(b *testing.B) {
function BenchmarkTopic10MentionAdminFullPageRouteParallelWithRouter (line 1038) | func BenchmarkTopic10MentionAdminFullPageRouteParallelWithRouter(b *test...
function BenchmarkTopic10MentionGuestFullPageRouteParallelWithRouter (line 1048) | func BenchmarkTopic10MentionGuestFullPageRouteParallelWithRouter(b *test...
function BenchmarkPopulateTopic1ReplyWithRouter (line 1055) | func BenchmarkPopulateTopic1ReplyWithRouter(b *testing.B) {
function BenchmarkTopic1ReplyAdminRouteParallelWithRouter (line 1080) | func BenchmarkTopic1ReplyAdminRouteParallelWithRouter(b *testing.B) {
function BenchmarkTopic1ReplyGuestRouteParallelWithRouter (line 1090) | func BenchmarkTopic1ReplyGuestRouteParallelWithRouter(b *testing.B) {
function BenchmarkPopulateTopic2UserWithRouter (line 1098) | func BenchmarkPopulateTopic2UserWithRouter(b *testing.B) {
function BenchmarkTopic2UserAdminFullPageRouteParallelWithRouter (line 1137) | func BenchmarkTopic2UserAdminFullPageRouteParallelWithRouter(b *testing....
function BenchmarkTopic2UserGuestFullPageRouteParallelWithRouter (line 1147) | func BenchmarkTopic2UserGuestFullPageRouteParallelWithRouter(b *testing....
function BenchmarkPopulateTopic3UserWithRouter (line 1154) | func BenchmarkPopulateTopic3UserWithRouter(b *testing.B) {
function BenchmarkTopic3UserAdminFullPageRouteParallelWithRouter (line 1196) | func BenchmarkTopic3UserAdminFullPageRouteParallelWithRouter(b *testing....
function BenchmarkTopic3UserGuestFullPageRouteParallelWithRouter (line 1206) | func BenchmarkTopic3UserGuestFullPageRouteParallelWithRouter(b *testing....
function BenchmarkQueryTopicParallel (line 1509) | func BenchmarkQueryTopicParallel(b *testing.B) {
function BenchmarkQueryPreparedTopicParallel (line 1530) | func BenchmarkQueryPreparedTopicParallel(b *testing.B) {
function BenchmarkUserGet (line 1558) | func BenchmarkUserGet(b *testing.B) {
function BenchmarkUserBypassGet (line 1576) | func BenchmarkUserBypassGet(b *testing.B) {
function BenchmarkQueriesSerial (line 1595) | func BenchmarkQueriesSerial(b *testing.B) {
function BenchmarkParserSerial (line 1656) | func BenchmarkParserSerial(b *testing.B) {
function BenchmarkBBCodePluginWithRegexpSerial (line 1676) | func BenchmarkBBCodePluginWithRegexpSerial(b *testing.B) {
function BenchmarkBBCodePluginWithoutCodeTagSerial (line 1699) | func BenchmarkBBCodePluginWithoutCodeTagSerial(b *testing.B) {
function BenchmarkBBCodePluginWithFullParserSerial (line 1719) | func BenchmarkBBCodePluginWithFullParserSerial(b *testing.B) {
function TestLevels (line 1739) | func TestLevels(t *testing.T) {
function TestSplittyThing (line 1936) | func TestSplittyThing(t *testing.T) {
FILE: install/install.go
type InstallAdapter (line 11) | type InstallAdapter interface
function Lookup (line 27) | func Lookup(name string) (InstallAdapter, bool) {
function createAdmin (line 32) | func createAdmin() error {
FILE: install/mssql.go
function init (line 24) | func init() {
type MssqlInstaller (line 28) | type MssqlInstaller struct
method SetConfig (line 38) | func (ins *MssqlInstaller) SetConfig(dbHost string, dbUsername string,...
method Name (line 47) | func (ins *MssqlInstaller) Name() string {
method DefaultPort (line 51) | func (ins *MssqlInstaller) DefaultPort() string {
method InitDatabase (line 55) | func (ins *MssqlInstaller) InitDatabase() (err error) {
method TableDefs (line 87) | func (ins *MssqlInstaller) TableDefs() (err error) {
method InitialData (line 126) | func (ins *MssqlInstaller) InitialData() (err error) {
method CreateAdmin (line 149) | func (ins *MssqlInstaller) CreateAdmin() error {
method DBHost (line 153) | func (ins *MssqlInstaller) DBHost() string {
method DBUsername (line 157) | func (ins *MssqlInstaller) DBUsername() string {
method DBPassword (line 161) | func (ins *MssqlInstaller) DBPassword() string {
method DBName (line 165) | func (ins *MssqlInstaller) DBName() string {
method DBPort (line 169) | func (ins *MssqlInstaller) DBPort() string {
FILE: install/mysql.go
function init (line 25) | func init() {
type MysqlInstaller (line 29) | type MysqlInstaller struct
method SetConfig (line 38) | func (ins *MysqlInstaller) SetConfig(dbHost string, dbUsername string,...
method Name (line 46) | func (ins *MysqlInstaller) Name() string {
method DefaultPort (line 50) | func (ins *MysqlInstaller) DefaultPort() string {
method dbExists (line 54) | func (ins *MysqlInstaller) dbExists(dbName string) (bool, error) {
method InitDatabase (line 65) | func (ins *MysqlInstaller) InitDatabase() (err error) {
method createTable (line 122) | func (ins *MysqlInstaller) createTable(f os.FileInfo) error {
method TableDefs (line 157) | func (ins *MysqlInstaller) TableDefs() (err error) {
method InitialData (line 187) | func (ins *MysqlInstaller) InitialData() error {
method CreateAdmin (line 212) | func (ins *MysqlInstaller) CreateAdmin() error {
method DBHost (line 216) | func (ins *MysqlInstaller) DBHost() string {
method DBUsername (line 220) | func (ins *MysqlInstaller) DBUsername() string {
method DBPassword (line 224) | func (ins *MysqlInstaller) DBPassword() string {
method DBName (line 228) | func (ins *MysqlInstaller) DBName() string {
method DBPort (line 232) | func (ins *MysqlInstaller) DBPort() string {
FILE: install/pgsql.go
function init (line 23) | func init() {
type PgsqlInstaller (line 27) | type PgsqlInstaller struct
method SetConfig (line 36) | func (ins *PgsqlInstaller) SetConfig(dbHost string, dbUsername string,...
method Name (line 44) | func (ins *PgsqlInstaller) Name() string {
method DefaultPort (line 48) | func (ins *PgsqlInstaller) DefaultPort() string {
method InitDatabase (line 52) | func (ins *PgsqlInstaller) InitDatabase() (err error) {
method TableDefs (line 77) | func (ins *PgsqlInstaller) TableDefs() (err error) {
method InitialData (line 81) | func (ins *PgsqlInstaller) InitialData() (err error) {
method CreateAdmin (line 85) | func (ins *PgsqlInstaller) CreateAdmin() error {
method DBHost (line 89) | func (ins *PgsqlInstaller) DBHost() string {
method DBUsername (line 93) | func (ins *PgsqlInstaller) DBUsername() string {
method DBPassword (line 97) | func (ins *PgsqlInstaller) DBPassword() string {
method DBName (line 101) | func (ins *PgsqlInstaller) DBName() string {
method DBPort (line 105) | func (ins *PgsqlInstaller) DBPort() string {
function pgEscapeBit (line 109) | func pgEscapeBit(bit string) string {
FILE: install/utils.go
constant saltLength (line 7) | saltLength int = 32
function GenerateSafeString (line 10) | func GenerateSafeString(length int) (string, error) {
function BcryptGeneratePassword (line 21) | func BcryptGeneratePassword(password string) (hash string, salt string, ...
FILE: main.go
type Globs (line 48) | type Globs struct
function init (line 53) | func init() {
function afterDBInit (line 57) | func afterDBInit() (err error) {
function storeInit (line 129) | func storeInit() (e error) {
function main (line 359) | func main() {
function startServer (line 670) | func startServer() {
FILE: misc_test.go
function miscinit (line 23) | func miscinit(t *testing.T) {
function recordMustExist (line 29) | func recordMustExist(t *testing.T, err error, errmsg string, args ...int...
function recordMustNotExist (line 39) | func recordMustNotExist(t *testing.T, err error, errmsg string, args ......
function TestUserStore (line 49) | func TestUserStore(t *testing.T) {
function userStoreTest (line 65) | func userStoreTest(t *testing.T, newUserID int) {
function expectNilErr (line 418) | func expectNilErr(t *testing.T, item error) {
function expectIntToBeX (line 425) | func expectIntToBeX(t *testing.T, item, expect int, errmsg string) {
function expect (line 432) | func expect(t *testing.T, item bool, errmsg string) {
function expectf (line 439) | func expectf(t *testing.T, item bool, errmsg string, args ...interface{}) {
function exp (line 446) | func exp(t *testing.T) func(bool, string) {
function expf (line 455) | func expf(t *testing.T) func(bool, string, ...interface{}) {
function TestPermsMiddleware (line 464) | func TestPermsMiddleware(t *testing.T) {
function TestTopicStore (line 522) | func TestTopicStore(t *testing.T) {
function topicStoreTest (line 544) | func topicStoreTest(t *testing.T, newID int, ip string) {
function TestForumStore (line 648) | func TestForumStore(t *testing.T) {
function TestForumPermsStore (line 830) | func TestForumPermsStore(t *testing.T) {
function TestGroupStore (line 946) | func TestGroupStore(t *testing.T) {
function TestGroupPromotions (line 1071) | func TestGroupPromotions(t *testing.T) {
function TestReplyStore (line 1130) | func TestReplyStore(t *testing.T) {
function testReplyStore (line 1146) | func testReplyStore(t *testing.T, newID int, ip string) {
function TestLikes (line 1255) | func TestLikes(t *testing.T) {
function TestAttachments (line 1314) | func TestAttachments(t *testing.T) {
function TestPolls (line 1488) | func TestPolls(t *testing.T) {
function TestSearch (line 1603) | func TestSearch(t *testing.T) {
function TestProfileReplyStore (line 1638) | func TestProfileReplyStore(t *testing.T) {
function testProfileReplyStore (line 1656) | func testProfileReplyStore(t *testing.T, newID int, ip string) {
function TestConvos (line 1694) | func TestConvos(t *testing.T) {
function TestActivityStream (line 1853) | func TestActivityStream(t *testing.T) {
function TestLogs (line 1916) | func TestLogs(t *testing.T) {
function TestRegLogs (line 1949) | func TestRegLogs(t *testing.T) {
function TestLoginLogs (line 2019) | func TestLoginLogs(t *testing.T) {
function TestPluginManager (line 2106) | func TestPluginManager(t *testing.T) {
function TestPhrases (line 2260) | func TestPhrases(t *testing.T) {
function TestMetaStore (line 2274) | func TestMetaStore(t *testing.T) {
function TestPages (line 2301) | func TestPages(t *testing.T) {
function TestWordFilters (line 2351) | func TestWordFilters(t *testing.T) {
function TestMFAStore (line 2418) | func TestMFAStore(t *testing.T) {
function TestSlugs (line 2470) | func TestSlugs(t *testing.T) {
function TestWidgets (line 2505) | func TestWidgets(t *testing.T) {
function TestForumActions (line 2572) | func TestForumActions(t *testing.T) {
function TestTopicList (line 2731) | func TestTopicList(t *testing.T) {
function TestUtils (line 2889) | func TestUtils(t *testing.T) {
function TestWeakPassword (line 2909) | func TestWeakPassword(t *testing.T) {
function TestAuth (line 2961) | func TestAuth(t *testing.T) {
function passwordTest (line 3022) | func passwordTest(t *testing.T, realPassword, hashedPassword string) {
function TestUserPrivacy (line 3057) | func TestUserPrivacy(t *testing.T) {
type METri (line 3162) | type METri struct
type METriList (line 3168) | type METriList struct
method Add (line 3172) | func (l *METriList) Add(args ...string) {
type CountTest (line 3183) | type CountTest struct
type CountTestList (line 3189) | type CountTestList struct
method Add (line 3193) | func (l *CountTestList) Add(name, msg string, expects int) {
function TestWordCount (line 3197) | func TestWordCount(t *testing.T) {
function TestTick (line 3235) | func TestTick(t *testing.T) {
function TestWSHub (line 3254) | func TestWSHub(t *testing.T) {
FILE: mssql.go
function init (line 23) | func init() {
function initMSSQL (line 28) | func initMSSQL() (err error) {
FILE: mysql.go
function init (line 22) | func init() {
function initMySQL (line 27) | func initMySQL() (err error) {
FILE: old_router.go
type Router (line 15) | type Router struct
method Handle (line 28) | func (router *Router) Handle(pattern string, handle http.Handler) {
method HandleFunc (line 35) | func (router *Router) HandleFunc(pattern string, handle func(http.Resp...
method ServeHTTP (line 42) | func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Reque...
function NewRouter (line 21) | func NewRouter() *Router {
FILE: parser_test.go
function TestPreparser (line 12) | func TestPreparser(t *testing.T) {
function TestParser (line 146) | func TestParser(t *testing.T) {
function TestPaginate (line 494) | func TestPaginate(t *testing.T) {
FILE: patcher/main.go
function addPatch (line 21) | func addPatch(index int, handle func(*bufio.Scanner) error) {
function main (line 25) | func main() {
function pressAnyKey (line 66) | func pressAnyKey(scanner *bufio.Scanner) {
function prepMySQL (line 74) | func prepMySQL() error {
type SchemaFile (line 85) | type SchemaFile struct
function loadSchema (line 92) | func loadSchema() (schemaFile SchemaFile, err error) {
function patcher (line 102) | func patcher(scanner *bufio.Scanner) error {
FILE: patcher/patches.go
function init (line 19) | func init() {
function bcol (line 59) | func bcol(col string, val bool) qgen.DBTableColumn {
function ccol (line 65) | func ccol(col string, size int, sdefault string) qgen.DBTableColumn {
function patch0 (line 69) | func patch0(scanner *bufio.Scanner) (err error) {
function patch1 (line 171) | func patch1(scanner *bufio.Scanner) error {
function patch2 (line 181) | func patch2(scanner *bufio.Scanner) error {
function patch3 (line 195) | func patch3(scanner *bufio.Scanner) error {
function patch4 (line 212) | func patch4(scanner *bufio.Scanner) error {
function patch5 (line 275) | func patch5(scanner *bufio.Scanner) error {
function patch6 (line 313) | func patch6(scanner *bufio.Scanner) error {
function patch7 (line 317) | func patch7(scanner *bufio.Scanner) error {
function renameRoutes (line 328) | func renameRoutes(routes map[string]string) error {
function patch8 (line 344) | func patch8(scanner *bufio.Scanner) error {
function patch9 (line 384) | func patch9(scanner *bufio.Scanner) error {
function patch10 (line 408) | func patch10(scanner *bufio.Scanner) error {
function patch11 (line 447) | func patch11(scanner *bufio.Scanner) error {
function patch12 (line 481) | func patch12(scanner *bufio.Scanner) error {
function patch13 (line 504) | func patch13(scanner *bufio.Scanner) error {
function patch14 (line 508) | func patch14(scanner *bufio.Scanner) error {
function patch15 (line 525) | func patch15(scanner *bufio.Scanner) error {
function patch16 (line 529) | func patch16(scanner *bufio.Scanner) error {
function patch17 (line 541) | func patch17(scanner *bufio.Scanner) error {
function patch18 (line 575) | func patch18(scanner *bufio.Scanner) error {
function patch19 (line 579) | func patch19(scanner *bufio.Scanner) error {
function patch20 (line 588) | func patch20(scanner *bufio.Scanner) error {
function patch21 (line 608) | func patch21(scanner *bufio.Scanner) error {
function patch22 (line 632) | func patch22(scanner *bufio.Scanner) error {
function patch23 (line 636) | func patch23(scanner *bufio.Scanner) error {
function patch24 (line 677) | func patch24(scanner *bufio.Scanner) error {
function patch25 (line 695) | func patch25(scanner *bufio.Scanner) error {
function patch26 (line 699) | func patch26(scanner *bufio.Scanner) error {
function patch27 (line 708) | func patch27(scanner *bufio.Scanner) error {
function patch28 (line 716) | func patch28(scanner *bufio.Scanner) error {
function WordCount (line 721) | func WordCount(input string) (count int) {
function patch29 (line 742) | func patch29(scanner *bufio.Scanner) error {
function patch30 (line 813) | func patch30(scanner *bufio.Scanner) error {
function patch31 (line 821) | func patch31(scanner *bufio.Scanner) (e error) {
function createTable (line 840) | func createTable(tbl, charset, collation string, cols []tC, keys []tK) e...
function patch32 (line 848) | func patch32(scanner *bufio.Scanner) error {
function patch33 (line 859) | func patch33(scanner *bufio.Scanner) error {
function patch34 (line 863) | func patch34(scanner *bufio.Scanner) error {
function patch35 (line 935) | func patch35(scanner *bufio.Scanner) error {
function patch36 (line 943) | func patch36(scanner *bufio.Scanner) error {
FILE: patcher/utils.go
function execStmt (line 6) | func execStmt(stmt *sql.Stmt, err error) error {
function eachUser (line 34) | func eachUser(handle func(int) error) error {
FILE: pgsql.go
function init (line 19) | func init() {
function initPgsql (line 24) | func initPgsql() (err error) {
function _escape_bit (line 67) | func _escape_bit(bit string) string {
FILE: plugin_test.go
type MEPair (line 15) | type MEPair struct
type MEPairList (line 20) | type MEPairList struct
method Add (line 24) | func (l *MEPairList) Add(msg, expects string) {
function TestBBCodeRender (line 28) | func TestBBCodeRender(t *testing.T) {
function TestMarkdownRender (line 185) | func TestMarkdownRender(t *testing.T) {
FILE: public/EQCSS.js
function l (line 648) | function l(a) { console.log(a) }
FILE: public/Sortable-1.4.0/Sortable.js
function Sortable (line 174) | function Sortable(el, options) {
function _cloneHide (line 970) | function _cloneHide(state) {
function _closest (line 979) | function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElemen...
function _globalDragOver (line 1004) | function _globalDragOver(/**Event*/evt) {
function _on (line 1012) | function _on(el, event, fn) {
function _off (line 1017) | function _off(el, event, fn) {
function _toggleClass (line 1022) | function _toggleClass(el, name, state) {
function _css (line 1035) | function _css(el, prop, val) {
function _find (line 1060) | function _find(ctx, tagName, iterator) {
function _dispatchEvent (line 1078) | function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startI...
function _onMove (line 1101) | function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) {
function _disableDraggable (line 1127) | function _disableDraggable(el) {
function _unsilent (line 1132) | function _unsilent() {
function _ghostIsLast (line 1138) | function _ghostIsLast(el, evt) {
function _generateId (line 1152) | function _generateId(el) {
function _index (line 1169) | function _index(el) {
function _throttle (line 1185) | function _throttle(callback, ms) {
function _extend (line 1206) | function _extend(dst, src) {
FILE: public/analytics.js
function memStuff (line 1) | function memStuff(window,document,Chartist) {
function perfStuff (line 39) | function perfStuff(window,document,Chartist) {
function convertByteUnit (line 83) | function convertByteUnit(bytes, places = 0) {
function convertPerfUnit (line 104) | function convertPerfUnit(quan, places = 0) {
function buildStatsChart (line 122) | function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, ...
FILE: public/global.js
function pushNotice (line 18) | function pushNotice(msg) {
function ajaxError (line 27) | function ajaxError(xhr,status,e) {
function postLink (line 36) | function postLink(ev) {
function bindToAlerts (line 42) | function bindToAlerts() {
function addAlert (line 61) | function addAlert(msg,notice=false) {
function updateAlertList (line 95) | function updateAlertList(menuAlerts) {
function setAlertError (line 130) | function setAlertError(menuAlerts,msg) {
function loadAlerts (line 137) | function loadAlerts(menuAlerts,eTc=false) {
function SplitN (line 188) | function SplitN(data,ch,n) {
function wsAlertEvent (line 207) | function wsAlertEvent(dat) {
function runWebSockets (line 223) | function runWebSockets(resume=false) {
function getExt (line 368) | function getExt(name) {
function PageOffset (line 425) | function PageOffset(count,page,perPage) {
function LastPage (line 438) | function LastPage(count,perPage) {
function Paginate (line 441) | function Paginate(currentPage,lastPage,maxPages) {
function mainInit (line 456) | function mainInit(){
function bindPage (line 949) | function bindPage() {
function unbindPage (line 964) | function unbindPage() {
function bindTopic (line 972) | function bindTopic() {
function unbindTopic (line 1132) | function unbindTopic() {
FILE: public/init.js
function runHook (line 11) | function runHook(name,...args) {
function addHook (line 23) | function addHook(name,h) {
function runInitHook (line 30) | function runInitHook(name,...args) {
function addInitHook (line 34) | function addInitHook(name,h) {
function len (line 43) | function len(d) {return d.length}
function asyncGetScript (line 45) | function asyncGetScript(src) {
function notifyOnScript (line 71) | function notifyOnScript(src) {
function notifyOnScriptW (line 105) | function notifyOnScriptW(name,complete,success) {
function loadScript (line 119) | function loadScript(name,h,fail) {
function RelativeTime (line 142) | function RelativeTime(date) {return date}
function initPhrases (line 144) | function initPhrases(member,acp=false) {
function fetchPhrases (line 154) | function fetchPhrases(plist) {
FILE: public/member.js
function copyToClipboard (line 5) | function copyToClipboard(str) {
function uploadFileHandler (line 17) | function uploadFileHandler(fileList, maxFiles=5, step1 = () => {}, step2...
function uploadAttachHandler2 (line 56) | function uploadAttachHandler2() {
function uploadAttachHandler (line 101) | function uploadAttachHandler() {
function bindAttachManager (line 140) | function bindAttachManager() {
function bindAttachItems (line 168) | function bindAttachItems() {
function addPollInput (line 218) | function addPollInput() {
function modCancel (line 244) | function modCancel() {
function modCancelBind (line 257) | function modCancelBind() {
function modLinkBind (line 266) | function modLinkBind() {
FILE: public/profile_member.js
function handle_profile_hashbit (line 1) | function handle_profile_hashbit() {
FILE: pubnot/chartist/chartist.js
function recursiveConvert (line 454) | function recursiveConvert(value) {
function recursiveHighLow (line 583) | function recursiveHighLow(data) {
function gcd (line 710) | function gcd(p, q) {
function f (line 718) | function f(x) {
function safeIncrement (line 801) | function safeIncrement(value, increment) {
function updateCurrentOptions (line 1054) | function updateCurrentOptions(mediaEvent) {
function removeMediaQueryListeners (line 1075) | function removeMediaQueryListeners() {
function addEventHandler (line 1627) | function addEventHandler(event, handler) {
function removeEventHandler (line 1639) | function removeEventHandler(event, handler) {
function emit (line 1662) | function emit(event, data) {
function listToArray (line 1695) | function listToArray(list) {
function extend (line 1746) | function extend(properties, superProtoOverride) {
function cloneDefinitions (line 1774) | function cloneDefinitions() {
function update (line 1820) | function update(data, options, override) {
function detach (line 1857) | function detach() {
function on (line 1877) | function on(event, handler) {
function off (line 1889) | function off(event, handler) {
function initialize (line 1894) | function initialize() {
function Base (line 1942) | function Base(query, data, defaultOptions, options, responsiveOptions) {
function Svg (line 2010) | function Svg(name, attributes, className, parent, insertFirst) {
function attr (line 2050) | function attr(attributes, ns) {
function elem (line 2086) | function elem(name, attributes, className, insertFirst) {
function parent (line 2096) | function parent() {
function root (line 2106) | function root() {
function querySelector (line 2121) | function querySelector(selector) {
function querySelectorAll (line 2133) | function querySelectorAll(selector) {
function getNode (line 2144) | function getNode() {
function foreignObject (line 2158) | function foreignObject(content, attributes, className, insertFirst) {
function text (line 2187) | function text(t) {
function empty (line 2198) | function empty() {
function remove (line 2212) | function remove() {
function replace (line 2224) | function replace(newElement) {
function append (line 2237) | function append(element, insertFirst) {
function classes (line 2253) | function classes() {
function addClass (line 2264) | function addClass(names) {
function removeClass (line 2283) | function removeClass(names) {
function removeAllClasses (line 2299) | function removeAllClasses() {
function height (line 2311) | function height() {
function width (line 2321) | function width() {
function animate (line 2366) | function animate(animations, guided, eventEmitter) {
function SvgList (line 2554) | function SvgList(nodeList) {
function element (line 2621) | function element(command, params, pathElements, pos, relative, data) {
function forEachParam (line 2629) | function forEachParam(pathElements, cb) {
function SvgPath (line 2645) | function SvgPath(close, options) {
function position (line 2659) | function position(pos) {
function remove (line 2675) | function remove(count) {
function move (line 2690) | function move(x, y, relative, data) {
function line (line 2708) | function line(x, y, relative, data) {
function curve (line 2730) | function curve(x1, y1, x2, y2, x, y, relative, data) {
function arc (line 2757) | function arc(rx, ry, xAr, lAf, sf, x, y, relative, data) {
function parse (line 2777) | function parse(path) {
function stringify (line 2826) | function stringify() {
function scale (line 2848) | function scale(x, y) {
function translate (line 2863) | function translate(x, y) {
function transform (line 2882) | function transform(transformFnc) {
function clone (line 2899) | function clone(close) {
function splitByCommand (line 2916) | function splitByCommand(command) {
function join (line 2942) | function join(paths, close, options) {
function Axis (line 2996) | function Axis(units, chartRect, ticks, options) {
function createGridAndLabels (line 3006) | function createGridAndLabels(gridGroup, labelGroup, useForeignObject, ch...
function AutoScaleAxis (line 3114) | function AutoScaleAxis(axisUnit, data, chartRect, options) {
function projectValue (line 3130) | function projectValue(value) {
function FixedScaleAxis (line 3163) | function FixedScaleAxis(axisUnit, data, chartRect, options) {
function projectValue (line 3186) | function projectValue(value) {
function StepAxis (line 3215) | function StepAxis(axisUnit, data, chartRect, options) {
function projectValue (line 3226) | function projectValue(value, index) {
function createChart (line 3351) | function createChart(options) {
function Line (line 3635) | function Line(query, data, options, responsiveOptions) {
function createChart (line 3764) | function createChart(options) {
function Bar (line 4078) | function Bar(query, data, options, responsiveOptions) {
function determineAnchorPosition (line 4160) | function determineAnchorPosition(center, label, direction) {
function createChart (line 4179) | function createChart(options) {
function Pie (line 4468) | function Pie(query, data, options, responsiveOptions) {
FILE: pubnot/trumbowyg/plugins/cleanpaste/trumbowyg.cleanpaste.js
function reverse (line 16) | function reverse(sentString) {
function checkValidTags (line 24) | function checkValidTags(snippet) {
function cleanIt (line 49) | function cleanIt(htmlBefore, htmlAfter) {
FILE: pubnot/trumbowyg/plugins/colors/trumbowyg.colors.js
function hex (line 50) | function hex(x) {
function colorToHex (line 54) | function colorToHex(rgb) {
function colorTagHandler (line 65) | function colorTagHandler(element, trumbowyg) {
function buildDropdown (line 125) | function buildDropdown(fn, trumbowyg) {
FILE: pubnot/trumbowyg/plugins/emoji/trumbowyg.emoji.js
function buildDropdown (line 927) | function buildDropdown(trumbowyg) {
FILE: pubnot/trumbowyg/plugins/preformatted/trumbowyg.preformatted.js
function getSelectionParentElement (line 69) | function getSelectionParentElement() {
function strip (line 90) | function strip(html) {
function unwrapCode (line 101) | function unwrapCode() {
FILE: pubnot/trumbowyg/plugins/template/trumbowyg.template.js
function templateSelector (line 41) | function templateSelector(trumbowyg) {
FILE: pubnot/trumbowyg/plugins/upload/trumbowyg.upload.js
function getDeep (line 29) | function getDeep(object, propertyParts) {
function addXhrProgressEvent (line 223) | function addXhrProgressEvent() {
FILE: query_gen/acc_builders.go
type accDeleteBuilder (line 11) | type accDeleteBuilder struct
method Where (line 19) | func (b *accDeleteBuilder) Where(w string) *accDeleteBuilder {
method DateCutoff (line 27) | func (b *accDeleteBuilder) DateCutoff(col string, quantity int, unit s...
method DateOlderThan (line 32) | func (b *accDeleteBuilder) DateOlderThan(col string, quantity int, uni...
method DateOlderThanQ (line 37) | func (b *accDeleteBuilder) DateOlderThanQ(col, unit string) *accDelete...
method Prepare (line 47) | func (b *accDeleteBuilder) Prepare() *sql.Stmt {
method Exec (line 56) | func (b *accDeleteBuilder) Exec(args ...interface{}) (res sql.Result, ...
method Run (line 64) | func (b *accDeleteBuilder) Run(args ...interface{}) (int, error) {
type accUpdateBuilder (line 73) | type accUpdateBuilder struct
method Set (line 78) | func (u *accUpdateBuilder) Set(set string) *accUpdateBuilder {
method Where (line 83) | func (u *accUpdateBuilder) Where(where string) *accUpdateBuilder {
method DateCutoff (line 91) | func (b *accUpdateBuilder) DateCutoff(col string, quantity int, unit s...
method DateOlderThan (line 96) | func (b *accUpdateBuilder) DateOlderThan(col string, quantity int, uni...
method DateOlderThanQ (line 101) | func (b *accUpdateBuilder) DateOlderThanQ(col, unit string) *accUpdate...
method WhereQ (line 106) | func (b *accUpdateBuilder) WhereQ(sel *selectPrebuilder) *accUpdateBui...
method Prepare (line 111) | func (b *accUpdateBuilder) Prepare() *sql.Stmt {
method Stmt (line 117) | func (b *accUpdateBuilder) Stmt() *sql.Stmt {
method Exec (line 124) | func (b *accUpdateBuilder) Exec(args ...interface{}) (res sql.Result, ...
type AccBuilder (line 133) | type AccBuilder interface
type AccExec (line 137) | type AccExec interface
type AccSelectBuilder (line 141) | type AccSelectBuilder struct
method Columns (line 154) | func (b *AccSelectBuilder) Columns(cols string) *AccSelectBuilder {
method Cols (line 159) | func (b *AccSelectBuilder) Cols(cols string) *AccSelectBuilder {
method Where (line 164) | func (b *AccSelectBuilder) Where(where string) *AccSelectBuilder {
method In (line 173) | func (b *AccSelectBuilder) In(col string, inList []int) *AccSelectBuil...
method InPQuery (line 200) | func (b *AccSelectBuilder) InPQuery(col string, inList []int) (*sql.Ro...
method InQ (line 222) | func (b *AccSelectBuilder) InQ(col string, sb *AccSelectBuilder) *AccS...
method DateCutoff (line 228) | func (b *AccSelectBuilder) DateCutoff(col string, quantity int, unit s...
method DateOlderThanQ (line 233) | func (b *AccSelectBuilder) DateOlderThanQ(col, unit string) *AccSelect...
method Orderby (line 238) | func (b *AccSelectBuilder) Orderby(orderby string) *AccSelectBuilder {
method Limit (line 243) | func (b *AccSelectBuilder) Limit(limit string) *AccSelectBuilder {
method Prepare (line 248) | func (b *AccSelectBuilder) Prepare() *sql.Stmt {
method Stmt (line 257) | func (b *AccSelectBuilder) Stmt() *sql.Stmt {
method ComplexPrepare (line 266) | func (b *AccSelectBuilder) ComplexPrepare() *sql.Stmt {
method query (line 271) | func (b *AccSelectBuilder) query() (string, error) {
method Query (line 280) | func (b *AccSelectBuilder) Query(args ...interface{}) (*sql.Rows, erro...
method QueryRow (line 301) | func (b *AccSelectBuilder) QueryRow(args ...interface{}) *AccRowWrap {
method Each (line 310) | func (b *AccSelectBuilder) Each(h func(*sql.Rows) error) error {
method EachP (line 327) | func (b *AccSelectBuilder) EachP(h func(*sql.Rows) error, p ...interfa...
method EachInt (line 344) | func (b *AccSelectBuilder) EachInt(h func(int) error) error {
type AccRowWrap (line 288) | type AccRowWrap struct
method Scan (line 293) | func (w *AccRowWrap) Scan(dest ...interface{}) error {
type accInsertBuilder (line 366) | type accInsertBuilder struct
method Columns (line 374) | func (b *accInsertBuilder) Columns(cols string) *accInsertBuilder {
method Fields (line 379) | func (b *accInsertBuilder) Fields(fields string) *accInsertBuilder {
method Prepare (line 384) | func (b *accInsertBuilder) Prepare() *sql.Stmt {
method Exec (line 388) | func (b *accInsertBuilder) Exec(args ...interface{}) (res sql.Result, ...
method Run (line 396) | func (b *accInsertBuilder) Run(args ...interface{}) (int, error) {
type accBulkInsertBuilder (line 405) | type accBulkInsertBuilder struct
method Columns (line 413) | func (b *accBulkInsertBuilder) Columns(cols string) *accBulkInsertBuil...
method Fields (line 418) | func (b *accBulkInsertBuilder) Fields(fieldSet ...string) *accBulkInse...
method Prepare (line 423) | func (b *accBulkInsertBuilder) Prepare() *sql.Stmt {
method Exec (line 427) | func (b *accBulkInsertBuilder) Exec(args ...interface{}) (res sql.Resu...
method Run (line 435) | func (b *accBulkInsertBuilder) Run(args ...interface{}) (int, error) {
type accCountBuilder (line 444) | type accCountBuilder struct
method Where (line 455) | func (b *accCountBuilder) Where(w string) *accCountBuilder {
method Limit (line 463) | func (b *accCountBuilder) Limit(limit string) *accCountBuilder {
method DateCutoff (line 468) | func (b *accCountBuilder) DateCutoff(col string, quantity int, unit st...
method DateOlderThanQ (line 473) | func (b *accCountBuilder) DateOlderThanQ(col, unit string) *accCountBu...
method Prepare (line 479) | func (b *accCountBuilder) Prepare() *sql.Stmt {
method Stmt (line 489) | func (b *accCountBuilder) Stmt() *sql.Stmt {
method Total (line 499) | func (b *accCountBuilder) Total() (total int, e error) {
method TotalP (line 508) | func (b *accCountBuilder) TotalP(params ...interface{}) (total int, e ...
FILE: query_gen/accumulator.go
function NewAcc (line 13) | func NewAcc() *Accumulator {
type Accumulator (line 17) | type Accumulator struct
method SetConn (line 23) | func (acc *Accumulator) SetConn(conn *sql.DB) {
method SetAdapter (line 27) | func (acc *Accumulator) SetAdapter(name string) error {
method GetAdapter (line 36) | func (acc *Accumulator) GetAdapter() Adapter {
method FirstError (line 40) | func (acc *Accumulator) FirstError() error {
method RecordError (line 44) | func (acc *Accumulator) RecordError(err error) {
method prepare (line 53) | func (acc *Accumulator) prepare(res string, err error) *sql.Stmt {
method RawPrepare (line 67) | func (acc *Accumulator) RawPrepare(res string) *sql.Stmt {
method query (line 71) | func (acc *Accumulator) query(q string, args ...interface{}) (rows *sq...
method exec (line 79) | func (acc *Accumulator) exec(q string, args ...interface{}) (res sql.R...
method Tx (line 87) | func (acc *Accumulator) Tx(handler func(*TransactionBuilder) error) {
method SimpleSelect (line 102) | func (acc *Accumulator) SimpleSelect(table, columns, where, orderby, l...
method SimpleCount (line 106) | func (acc *Accumulator) SimpleCount(table, where, limit string) *sql.S...
method SimpleLeftJoin (line 110) | func (acc *Accumulator) SimpleLeftJoin(table1, table2, columns, joiner...
method SimpleInnerJoin (line 114) | func (acc *Accumulator) SimpleInnerJoin(table1, table2, columns, joine...
method CreateTable (line 118) | func (acc *Accumulator) CreateTable(table, charset, collation string, ...
method SimpleInsert (line 122) | func (acc *Accumulator) SimpleInsert(table, columns, fields string) *s...
method SimpleBulkInsert (line 126) | func (acc *Accumulator) SimpleBulkInsert(table, cols string, fieldSet ...
method SimpleInsertSelect (line 130) | func (acc *Accumulator) SimpleInsertSelect(ins DBInsert, sel DBSelect)...
method SimpleInsertLeftJoin (line 134) | func (acc *Accumulator) SimpleInsertLeftJoin(ins DBInsert, sel DBJoin)...
method SimpleInsertInnerJoin (line 138) | func (acc *Accumulator) SimpleInsertInnerJoin(ins DBInsert, sel DBJoin...
method SimpleUpdate (line 142) | func (acc *Accumulator) SimpleUpdate(table, set, where string) *sql.St...
method SimpleUpdateSelect (line 146) | func (acc *Accumulator) SimpleUpdateSelect(table, set, table2, cols, w...
method SimpleDelete (line 151) | func (acc *Accumulator) SimpleDelete(table, where string) *sql.Stmt {
method Purge (line 156) | func (acc *Accumulator) Purge(table string) *sql.Stmt {
method prepareTx (line 160) | func (acc *Accumulator) prepareTx(tx *sql.Tx, res string, err error) (...
method SimpleSelectTx (line 171) | func (acc *Accumulator) SimpleSelectTx(tx *sql.Tx, table, columns, whe...
method SimpleCountTx (line 176) | func (acc *Accumulator) SimpleCountTx(tx *sql.Tx, table, where, limit ...
method SimpleLeftJoinTx (line 181) | func (acc *Accumulator) SimpleLeftJoinTx(tx *sql.Tx, table1, table2, c...
method SimpleInnerJoinTx (line 186) | func (acc *Accumulator) SimpleInnerJoinTx(tx *sql.Tx, table1, table2, ...
method CreateTableTx (line 191) | func (acc *Accumulator) CreateTableTx(tx *sql.Tx, table, charset, coll...
method SimpleInsertTx (line 196) | func (acc *Accumulator) SimpleInsertTx(tx *sql.Tx, table, columns, fie...
method SimpleInsertSelectTx (line 201) | func (acc *Accumulator) SimpleInsertSelectTx(tx *sql.Tx, ins DBInsert,...
method SimpleInsertLeftJoinTx (line 206) | func (acc *Accumulator) SimpleInsertLeftJoinTx(tx *sql.Tx, ins DBInser...
method SimpleInsertInnerJoinTx (line 211) | func (acc *Accumulator) SimpleInsertInnerJoinTx(tx *sql.Tx, ins DBInse...
method SimpleUpdateTx (line 216) | func (acc *Accumulator) SimpleUpdateTx(tx *sql.Tx, table, set, where s...
method SimpleDeleteTx (line 221) | func (acc *Accumulator) SimpleDeleteTx(tx *sql.Tx, table, where string...
method PurgeTx (line 227) | func (acc *Accumulator) PurgeTx(tx *sql.Tx, table string) (stmt *sql.S...
method Delete (line 232) | func (acc *Accumulator) Delete(table string) *accDeleteBuilder {
method Update (line 236) | func (acc *Accumulator) Update(table string) *accUpdateBuilder {
method Select (line 240) | func (acc *Accumulator) Select(table string) *AccSelectBuilder {
method Exists (line 244) | func (acc *Accumulator) Exists(tbl, col string) *AccSelectBuilder {
method Insert (line 248) | func (acc *Accumulator) Insert(table string) *accInsertBuilder {
method BulkInsert (line 252) | func (acc *Accumulator) BulkInsert(table string) *accBulkInsertBuilder {
method Count (line 256) | func (acc *Accumulator) Count(table string) *accCountBuilder {
method SimpleModel (line 266) | func (acc *Accumulator) SimpleModel(tbl, colstr, primary string) Simpl...
method Model (line 309) | func (acc *Accumulator) Model(table string) *accModelBuilder {
type SimpleModel (line 260) | type SimpleModel struct
method Delete (line 285) | func (m SimpleModel) Delete(keyVal interface{}) error {
method Update (line 290) | func (m SimpleModel) Update(args ...interface{}) error {
method Create (line 295) | func (m SimpleModel) Create(args ...interface{}) error {
method CreateID (line 300) | func (m SimpleModel) CreateID(args ...interface{}) (int, error) {
type accModelBuilder (line 313) | type accModelBuilder struct
method Primary (line 320) | func (b *accModelBuilder) Primary(col string) *accModelBuilder {
FILE: query_gen/builder.go
function init (line 11) | func init() {
type builder (line 16) | type builder struct
method Accumulator (line 21) | func (b *builder) Accumulator() *Accumulator {
method Init (line 26) | func (b *builder) Init(adapter string, config map[string]string) error {
method SetConn (line 37) | func (b *builder) SetConn(conn *sql.DB) {
method GetConn (line 41) | func (b *builder) GetConn() *sql.DB {
method SetAdapter (line 45) | func (b *builder) SetAdapter(name string) error {
method GetAdapter (line 54) | func (b *builder) GetAdapter() Adapter {
method DbVersion (line 58) | func (b *builder) DbVersion() (dbVersion string) {
method Begin (line 63) | func (b *builder) Begin() (*sql.Tx, error) {
method Tx (line 67) | func (b *builder) Tx(h func(*TransactionBuilder) error) error {
method prepare (line 80) | func (b *builder) prepare(res string, err error) (*sql.Stmt, error) {
method SimpleSelect (line 87) | func (b *builder) SimpleSelect(table, columns, where, orderby, limit s...
method SimpleCount (line 91) | func (b *builder) SimpleCount(table, where, limit string) (stmt *sql.S...
method SimpleLeftJoin (line 95) | func (b *builder) SimpleLeftJoin(table1, table2, columns, joiners, whe...
method SimpleInnerJoin (line 99) | func (b *builder) SimpleInnerJoin(table1, table2, columns, joiners, wh...
method DropTable (line 103) | func (b *builder) DropTable(table string) (stmt *sql.Stmt, err error) {
method CreateTable (line 107) | func (build *builder) CreateTable(table, charset, collation string, co...
method AddColumn (line 111) | func (b *builder) AddColumn(table string, column DBTableColumn, key *D...
method DropColumn (line 115) | func (b *builder) DropColumn(table, colName string) (stmt *sql.Stmt, e...
method RenameColumn (line 119) | func (b *builder) RenameColumn(table, oldName, newName string) (stmt *...
method ChangeColumn (line 123) | func (b *builder) ChangeColumn(table, colName string, col DBTableColum...
method SetDefaultColumn (line 127) | func (b *builder) SetDefaultColumn(table, colName, colType, defaultStr...
method AddIndex (line 131) | func (b *builder) AddIndex(table, iname, colname string) (stmt *sql.St...
method AddKey (line 135) | func (b *builder) AddKey(table, column string, key DBTableKey) (stmt *...
method RemoveIndex (line 139) | func (b *builder) RemoveIndex(table, iname string) (stmt *sql.Stmt, er...
method AddForeignKey (line 143) | func (b *builder) AddForeignKey(table, column, ftable, fcolumn string,...
method SimpleInsert (line 147) | func (b *builder) SimpleInsert(table, columns, fields string) (stmt *s...
method SimpleInsertSelect (line 151) | func (b *builder) SimpleInsertSelect(ins DBInsert, sel DBSelect) (stmt...
method SimpleInsertLeftJoin (line 155) | func (b *builder) SimpleInsertLeftJoin(ins DBInsert, sel DBJoin) (stmt...
method SimpleInsertInnerJoin (line 159) | func (b *builder) SimpleInsertInnerJoin(ins DBInsert, sel DBJoin) (stm...
method SimpleUpdate (line 163) | func (b *builder) SimpleUpdate(table, set, where string) (stmt *sql.St...
method SimpleDelete (line 167) | func (b *builder) SimpleDelete(table, where string) (stmt *sql.Stmt, e...
method Purge (line 172) | func (b *builder) Purge(table string) (stmt *sql.Stmt, err error) {
method prepareTx (line 176) | func (b *builder) prepareTx(tx *sql.Tx, res string, err error) (*sql.S...
method SimpleSelectTx (line 184) | func (b *builder) SimpleSelectTx(tx *sql.Tx, table, columns, where, or...
method SimpleCountTx (line 189) | func (b *builder) SimpleCountTx(tx *sql.Tx, table, where, limit string...
method SimpleLeftJoinTx (line 194)
Condensed preview — 695 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,407K chars).
[
{
"path": ".codebeatignore",
"chars": 336,
"preview": "/public/chartist/**\n/public/trumbowyg/**\n/public/jquery-emojiarea/**\n/public/font-awesome-4.7.0/**\n/public/jquery-3.1.1."
},
{
"path": ".codeclimate.yml",
"chars": 250,
"preview": "exclude_patterns:\n- \"gen_*\"\n- \"schema/*\"\n- \"public/chartist/*\"\n- \"public/trumbowyg/*\"\n- \"public/jquery-emojiarea/*\"\n- \"p"
},
{
"path": ".eslintrc.json",
"chars": 665,
"preview": "{\n \"env\": {\n \"browser\": true,\n \"commonjs\": true,\n \"es6\": true,\n \"node\": false\n },\n "
},
{
"path": ".gitignore",
"chars": 399,
"preview": "tmp/*\n!tmp/filler.txt\ntmp2/*\ncert_test/*\ntmp.txt\nrun_notemplategen.bat\nbrun.bat\n\nattachs/*\n!attachs/filler.txt\nuploads/a"
},
{
"path": ".htaccess",
"chars": 195,
"preview": "# Gosora doesn't use Apache, this file is just here to stop Apache from blindly serving our config files, etc. when this"
},
{
"path": ".travis.yml",
"chars": 682,
"preview": "language: go\ngo:\n - \"1.13\"\n - \"1.14\"\n - \"1.15\"\n - \"1.16\"\n - master\nbefore_install:\n - cd $HOME\n - git clone https"
},
{
"path": ".vscode/settings.json",
"chars": 109,
"preview": "// Place your settings in this file to overwrite default and user settings.\n{\n\t\"editor.insertSpaces\": false\n}"
},
{
"path": "CONTRIBUTING.md",
"chars": 2832,
"preview": "# Contributing\n\nFirst and foremost, if you want to add a contribution, you'll have to open a pull request and to sign th"
},
{
"path": "LICENSE",
"chars": 35141,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README.md",
"chars": 11386,
"preview": "# Gosora  [\n\ntype HookVars struct {\n\tImports []string\n\tHooks []"
},
{
"path": "cmd/elasticsearch/setup.go",
"chars": 5639,
"preview": "// Work in progress\npackage main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\""
},
{
"path": "cmd/hook_gen/main.go",
"chars": 1748,
"preview": "// +build hookgen\n\npackage main // import \"github.com/Azareal/Gosora/hook_gen\"\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"runtime/debug\"\n"
},
{
"path": "cmd/hook_stub_gen/main.go",
"chars": 1114,
"preview": "package main // import \"github.com/Azareal/Gosora/hook_stub_gen\"\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\t\"runtime/debug\"\n\t\n\t"
},
{
"path": "cmd/install/install.go",
"chars": 7480,
"preview": "/*\n*\n* Gosora Installer\n* Copyright Azareal 2017 - 2019\n*\n */\npackage main\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"r"
},
{
"path": "cmd/query_gen/build.bat",
"chars": 178,
"preview": "@echo off\necho Building the query generator\ngo build -ldflags=\"-s -w\"\nif %errorlevel% neq 0 (\n\tpause\n\texit /b %errorleve"
},
{
"path": "cmd/query_gen/main.go",
"chars": 14491,
"preview": "/* WIP Under Construction */\npackage main // import \"github.com/Azareal/Gosora/query_gen\"\n\nimport (\n\t\"encoding/json\"\n\t\"f"
},
{
"path": "cmd/query_gen/run.bat",
"chars": 192,
"preview": "@echo off\necho Building the query generator\ngo build -ldflags=\"-s -w\"\nif %errorlevel% neq 0 (\n\tpause\n\texit /b %errorleve"
},
{
"path": "cmd/query_gen/spitter.go",
"chars": 1007,
"preview": "package main\n\nimport \"strings\"\nimport \"github.com/Azareal/Gosora/query_gen\"\n\ntype PrimaryKeySpitter struct {\n\tkeys map[s"
},
{
"path": "cmd/query_gen/tables.go",
"chars": 23906,
"preview": "package main\n\nimport qgen \"github.com/Azareal/Gosora/query_gen\"\n\nvar mysqlPre = \"utf8mb4\"\nvar mysqlCol = \"utf8mb4_genera"
},
{
"path": "common/activity_stream.go",
"chars": 3782,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Activity ActivityStream\n\nty"
},
{
"path": "common/activity_stream_matches.go",
"chars": 1390,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar ActivityMatches ActivityStr"
},
{
"path": "common/alerts/tmpls.go",
"chars": 207,
"preview": "package alerts\n\n// TODO: Move the other alert related stuff to package alerts, maybe move notification logic here too?\n\n"
},
{
"path": "common/alerts.go",
"chars": 10838,
"preview": "/*\n*\n* Gosora Alerts System\n* Copyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"s"
},
{
"path": "common/analytics.go",
"chars": 2592,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Analytics An"
},
{
"path": "common/attachments.go",
"chars": 9438,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\n\t//\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\tqgen \"github.com/Azarea"
},
{
"path": "common/audit_logs.go",
"chars": 3712,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar ModLogs LogStore\nva"
},
{
"path": "common/auth.go",
"chars": 12555,
"preview": "/*\n*\n* Gosora Authentication Interface\n* Copyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"crypto/sha256\"\n\t"
},
{
"path": "common/cache.go",
"chars": 1116,
"preview": "package common\n\nimport \"errors\"\n\n// nolint\n// ErrCacheDesync is thrown whenever a piece of data, for instance, a user is"
},
{
"path": "common/common.go",
"chars": 8868,
"preview": "/*\n*\n*\tGosora Common Resources\n*\tCopyright Azareal 2018 - 2020\n*\n */\npackage common // import \"github.com/Azareal/Gosora"
},
{
"path": "common/common_easyjson.tgo",
"chars": 12512,
"preview": "// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.\n\npackage common\n\nimport (\n\tjson \"encoding/json\"\n"
},
{
"path": "common/conversations.go",
"chars": 9492,
"preview": "package common\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t//\"log\"\n\n\t\"database/sql\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/query"
},
{
"path": "common/convos_posts.go",
"chars": 3239,
"preview": "package common\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"io\"\n)\n\nvar ConvoPostProcess Con"
},
{
"path": "common/counters/agents.go",
"chars": 1560,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azare"
},
{
"path": "common/counters/common.go",
"chars": 904,
"preview": "package counters\n\nimport \"sync\"\n\n// TODO: Make a neater API for this\nvar routeMapEnum map[string]int\nvar reverseRouteMap"
},
{
"path": "common/counters/forums.go",
"chars": 2785,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azareal/Goso"
},
{
"path": "common/counters/langs.go",
"chars": 2890,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azare"
},
{
"path": "common/counters/memory.go",
"chars": 2002,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"gith"
},
{
"path": "common/counters/performance.go",
"chars": 2633,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"math\"\n\t\"time\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azar"
},
{
"path": "common/counters/posts.go",
"chars": 1525,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azare"
},
{
"path": "common/counters/referrers.go",
"chars": 3532,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.c"
},
{
"path": "common/counters/requests.go",
"chars": 1727,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azare"
},
{
"path": "common/counters/routes.go",
"chars": 4461,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \""
},
{
"path": "common/counters/systems.go",
"chars": 1488,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azare"
},
{
"path": "common/counters/topics.go",
"chars": 1526,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\tqgen \"github.com/Azare"
},
{
"path": "common/counters/topics_views.go",
"chars": 6128,
"preview": "package counters\n\nimport (\n\t\"database/sql\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tc \"github.com/Azareal/"
},
{
"path": "common/disk.go",
"chars": 313,
"preview": "package common\n\nimport (\n\t\"path/filepath\"\n\t\"os\"\n)\n\nfunc DirSize(path string) (int, error) {\n\tvar size int64\n\terr := file"
},
{
"path": "common/email.go",
"chars": 3163,
"preview": "package common\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/mail\"\n\t\"net/smtp\"\n\t\"strings\"\n\n\tp \"github.com/Azareal/Gosora/common/p"
},
{
"path": "common/email_store.go",
"chars": 2517,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Emails EmailStore\n\ntype Ema"
},
{
"path": "common/errors.go",
"chars": 14189,
"preview": "package common\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\tp \"github.com/Az"
},
{
"path": "common/extend.go",
"chars": 19719,
"preview": "/*\n*\n* Gosora Plugin System\n* Copyright Azareal 2016 - 2021\n*\n */\npackage common\n\n// TODO: Break this file up into multi"
},
{
"path": "common/files.go",
"chars": 14414,
"preview": "package common\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n"
},
{
"path": "common/forum.go",
"chars": 4166,
"preview": "package common\n\nimport (\n\t//\"log\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\tqgen \"github.com/Azareal/Gosora/quer"
},
{
"path": "common/forum_actions.go",
"chars": 10856,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar ForumActi"
},
{
"path": "common/forum_perms.go",
"chars": 7888,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\n\t\"github.com/Azareal/Gosora/query_gen\"\n)\n\n// ? - Can we avoid"
},
{
"path": "common/forum_perms_store.go",
"chars": 5789,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"sync\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar FP"
},
{
"path": "common/forum_store.go",
"chars": 12364,
"preview": "/*\n*\n*\tGosora Forum Store\n* \tCopyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"lo"
},
{
"path": "common/gauth/authenticator.go",
"chars": 1995,
"preview": "// Google Authenticator 2FA\n// Borrowed from https://github.com/tilaklodha/google-authenticator, as we can't import it a"
},
{
"path": "common/group.go",
"chars": 2748,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar blankGroup"
},
{
"path": "common/group_store.go",
"chars": 8617,
"preview": "/* Under Heavy Construction */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"log\"\n\t\"sort\"\n\t\"strc"
},
{
"path": "common/ip_search.go",
"chars": 1850,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar IPSearch IPSearcher\n\ntype I"
},
{
"path": "common/likes.go",
"chars": 2584,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Likes LikeStore\n\ntype LikeS"
},
{
"path": "common/menu_item_store.go",
"chars": 526,
"preview": "package common\n\nimport \"sync\"\n\ntype DefaultMenuItemStore struct {\n\titems map[int]MenuItem\n\tlock sync.RWMutex\n}\n\nfunc Ne"
},
{
"path": "common/menu_store.go",
"chars": 1875,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"strconv\"\n\t\"sync/atomic\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar M"
},
{
"path": "common/menus.go",
"chars": 14061,
"preview": "package common\n\nimport (\n\t\"bytes\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/Azareal/"
},
{
"path": "common/meta/meta_store.go",
"chars": 1486,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\n// MetaStore is a simple key-va"
},
{
"path": "common/mfa_store.go",
"chars": 2688,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"strings\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar MFAsto"
},
{
"path": "common/misc_logs.go",
"chars": 5845,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar RegLogs RegLogStore"
},
{
"path": "common/module_ottojs.go",
"chars": 1880,
"preview": "/*\n*\n*\tOttoJS Plugin Module\n*\tCopyright Azareal 2016 - 2019\n*\n */\npackage common\n\nimport (\n\t\"errors\"\n\n\t\"github.com/rober"
},
{
"path": "common/no_websockets.go",
"chars": 902,
"preview": "// +build no_ws\n\npackage common\n\nimport \"errors\"\nimport \"net/http\"\n\n// TODO: Disable WebSockets on high load? Add a Cont"
},
{
"path": "common/null_reply_cache.go",
"chars": 1097,
"preview": "package common\n\n// NullReplyCache is a reply cache to be used when you don't want a cache and just want queries to passt"
},
{
"path": "common/null_topic_cache.go",
"chars": 1166,
"preview": "package common\n\n// NullTopicCache is a topic cache to be used when you don't want a cache and just want queries to passt"
},
{
"path": "common/null_user_cache.go",
"chars": 1267,
"preview": "package common\n\n// NullUserCache is a user cache to be used when you don't want a cache and just want queries to passthr"
},
{
"path": "common/page_store.go",
"chars": 4681,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"strconv\"\n\t\"strings\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\ntype Cust"
},
{
"path": "common/pages.go",
"chars": 15074,
"preview": "package common\n\nimport (\n\t\"html/template\"\n\t\"net/http\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\tp \"github.com/Azareal/Gosora/common/p"
},
{
"path": "common/parser.go",
"chars": 36344,
"preview": "package common\n\nimport (\n\t\"bytes\"\n\t//\"fmt\"\n\t//\"log\"\n\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unico"
},
{
"path": "common/password_reset.go",
"chars": 1882,
"preview": "package common\n\nimport (\n\t\"crypto/subtle\"\n\t\"database/sql\"\n\t\"errors\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar "
},
{
"path": "common/permissions.go",
"chars": 6291,
"preview": "package common\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\n\t\"github.com/Azareal/Gosora/common/phrases\"\n\tqgen \"github.com/Azareal/"
},
{
"path": "common/phrases/phrases.go",
"chars": 10214,
"preview": "/*\n*\n* Gosora Phrase System\n* Copyright Azareal 2017 - 2020\n*\n */\npackage phrases\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t"
},
{
"path": "common/pluginlangs.go",
"chars": 2686,
"preview": "package common\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n)\n\nvar pluginLangs = make(map[string]P"
},
{
"path": "common/poll.go",
"chars": 2809,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar pollStmts PollStmts\n\ntype P"
},
{
"path": "common/poll_cache.go",
"chars": 5389,
"preview": "package common\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// PollCache is an interface which spits out polls from a fast cache "
},
{
"path": "common/poll_store.go",
"chars": 6121,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"log\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/"
},
{
"path": "common/profile_reply.go",
"chars": 1835,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"html\"\n\t\"strconv\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar "
},
{
"path": "common/profile_reply_store.go",
"chars": 2199,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Prstore ProfileReplyStore\n\n"
},
{
"path": "common/promotions.go",
"chars": 4222,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t//\"log\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar GroupPromo"
},
{
"path": "common/ratelimit.go",
"chars": 2061,
"preview": "package common\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar ErrBadRateLimiter = errors.New(\"That rate limiter d"
},
{
"path": "common/recalc.go",
"chars": 4672,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t//\"log\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Recalc "
},
{
"path": "common/relations.go",
"chars": 4135,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar UserBlocks BlockStore\n\n//va"
},
{
"path": "common/reply.go",
"chars": 6406,
"preview": "/*\n*\n* Reply Resources File\n* Copyright Azareal 2016 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"h"
},
{
"path": "common/reply_cache.go",
"chars": 4456,
"preview": "package common\n\nimport (\n\t//\"log\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// ReplyCache is an interface which spits out replies from a"
},
{
"path": "common/reply_store.go",
"chars": 4084,
"preview": "package common\n\n//import \"log\"\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Rstore Reply"
},
{
"path": "common/report_store.go",
"chars": 1920,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\n// TODO: M"
},
{
"path": "common/routes_common.go",
"chars": 17404,
"preview": "package common\n\nimport (\n\t\"crypto/subtle\"\n\t\"html\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\""
},
{
"path": "common/search.go",
"chars": 4754,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Replie"
},
{
"path": "common/settings.go",
"chars": 4325,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\tqgen \"github.com/Azareal/Gosor"
},
{
"path": "common/site.go",
"chars": 11482,
"preview": "package common\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\""
},
{
"path": "common/statistics.go",
"chars": 999,
"preview": "package common\n\n// EXPERIMENTAL\nimport (\n\t\"errors\"\n)\n\nvar StatStore StatStoreInt\n\ntype StatStoreInt interface {\n\tLookupI"
},
{
"path": "common/subscription.go",
"chars": 1514,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Subscriptions SubscriptionS"
},
{
"path": "common/tasks.go",
"chars": 4993,
"preview": "/*\n*\n*\tGosora Task System\n*\tCopyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n\t\"time\"\n"
},
{
"path": "common/template_init.go",
"chars": 33696,
"preview": "package common\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"stri"
},
{
"path": "common/templates/context.go",
"chars": 3173,
"preview": "package tmpl\n\nimport (\n\t\"reflect\"\n)\n\n// For use in generated code\ntype FragLite struct {\n\tBody string\n}\n\ntype Fragment s"
},
{
"path": "common/templates/minifiers.go",
"chars": 1453,
"preview": "package tmpl\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n// TODO: Write unit tests for this\nfunc Minify(data string) string {\n\tda"
},
{
"path": "common/templates/templates.go",
"chars": 69153,
"preview": "package tmpl\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime/debug\"\n\t\"strconv\""
},
{
"path": "common/thaw.go",
"chars": 1213,
"preview": "package common\n\nimport (\n\t\"sync/atomic\"\n)\n\nvar TopicListThaw ThawInt\n\ntype ThawInt interface {\n\tThawed() bool\n\tThaw()\n\n\t"
},
{
"path": "common/theme.go",
"chars": 13337,
"preview": "/* Copyright Azareal 2016 - 2019 */\npackage common\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"database/sql\"\n\t\"encoding/base64"
},
{
"path": "common/theme_list.go",
"chars": 8463,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"html/template\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/htt"
},
{
"path": "common/thumbnailer.go",
"chars": 4465,
"preview": "package common\n\nimport (\n\t\"image\"\n\t\"image/gif\"\n\t\"image/jpeg\"\n\t\"image/png\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"golang.org/x/image/tiff\"\n\n"
},
{
"path": "common/tickloop.go",
"chars": 8463,
"preview": "package common\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/qu"
},
{
"path": "common/topic.go",
"chars": 37117,
"preview": "/*\n*\n*\tGosora Topic File\n*\tCopyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"html\"\n\t\"html/t"
},
{
"path": "common/topic_cache.go",
"chars": 4676,
"preview": "package common\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// TopicCache is an interface which spits out topics from a fast cach"
},
{
"path": "common/topic_list.go",
"chars": 24400,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n"
},
{
"path": "common/topic_store.go",
"chars": 9024,
"preview": "/*\n*\n*\tGosora Topic Store\n*\tCopyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"str"
},
{
"path": "common/user.go",
"chars": 23145,
"preview": "/*\n*\n*\tGosora User File\n*\tCopyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"strco"
},
{
"path": "common/user_cache.go",
"chars": 5957,
"preview": "package common\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// UserCache is an interface which spits out users from a fast cache "
},
{
"path": "common/user_store.go",
"chars": 14708,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"strconv\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n\t\"golang.org/"
},
{
"path": "common/utils.go",
"chars": 12952,
"preview": "/*\n*\n*\tUtility Functions And Stuff\n*\tCopyright Azareal 2017 - 2020\n*\n */\npackage common\n\nimport (\n\t\"crypto/rand\"\n\t\"encod"
},
{
"path": "common/weak_passwords.go",
"chars": 3812,
"preview": "package common\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n)\n\nvar weakPassStrings []string\nvar weakPassLit = ma"
},
{
"path": "common/websockets.go",
"chars": 12584,
"preview": "// +build !no_ws\n\n/*\n*\n*\tGosora WebSocket Subsystem\n*\tCopyright Azareal 2017 - 2021\n*\n */\npackage common\n\nimport (\n\t\"byt"
},
{
"path": "common/widget.go",
"chars": 4123,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\tqgen \"github.com/Azarea"
},
{
"path": "common/widget_search_and_filter.go",
"chars": 1062,
"preview": "package common\n\nimport \"errors\"\n\n// TODO: Move this into it's own package to make neater and tidier\ntype filterForum str"
},
{
"path": "common/widget_store.go",
"chars": 642,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"sync\"\n)\n\nvar Widgets *DefaultWidgetStore\n\ntype DefaultWidgetStore struct {\n\tw"
},
{
"path": "common/widget_wol.go",
"chars": 2478,
"preview": "package common\n\nimport (\n\t\"bytes\"\n\t//\"log\"\n\t\"net/http/httptest\"\n\n\tp \"github.com/Azareal/Gosora/common/phrases\"\n\tmin \"git"
},
{
"path": "common/widget_wol_context.go",
"chars": 698,
"preview": "package common\n\nimport \"github.com/Azareal/Gosora/common/phrases\"\n\nfunc wolContextRender(widget *Widget, hvars interface"
},
{
"path": "common/widgets.go",
"chars": 9655,
"preview": "/* Copyright Azareal 2017 - 2020 */\npackage common\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"strings"
},
{
"path": "common/word_filters.go",
"chars": 4430,
"preview": "package common\n\nimport (\n\t\"database/sql\"\n\t\"sync/atomic\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\n// TODO: Move so"
},
{
"path": "common/ws_hub.go",
"chars": 12747,
"preview": "package common\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\n// TODO: Rename thi"
},
{
"path": "common/ws_user.go",
"chars": 4835,
"preview": "package common\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar ErrNoneOnPage = errors.New(\"T"
},
{
"path": "config/config_example.json",
"chars": 1193,
"preview": "{\n\t\"Site\": {\n\t\t\"ShortName\":\"Exa\",\n\t\t\"Name\":\"Example\",\n\t\t\"URL\":\"localhost\",\n\t\t\"Port\":\"80\",\n\t\t\"EnableSsl\":false,\n\t\t\"Enable"
},
{
"path": "config/emoji_default.json",
"chars": 3190,
"preview": "{\n\t\"emojis\": [\n\t\t{\":grinning:\": \"😀\"},\n\t\t{\":grin:\": \"😁\"},\n\t\t{\":joy:\": \"😂\"},\n\t\t{\":rofl:\": \"🤣\"},\n\t\t{\":smiley:\": \"😃\"},\n\t\t{\":"
},
{
"path": "config/filler.txt",
"chars": 73,
"preview": "This file is here so that Git will include this folder in the repository."
},
{
"path": "config/weakpass_default.json",
"chars": 322,
"preview": "{\n\t\"contains\":[\n\t\t\"test\", \"123\", \"6969\", \"password\", \"qwerty\", \"fuck\", \"love\",\"1 2 3 4 5\"\n\t],\n\t\"literal\":[\n\t\t\"superman\","
},
{
"path": "database.go",
"chars": 2091,
"preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n\t\"github.com/pkg/errors\"\n)\n\nvar stm"
},
{
"path": "dev-update-linux",
"chars": 220,
"preview": "echo \"Updating the dependencies\"\n./update-deps-linux\n\necho \"Updating Gosora\"\ngit stash\ngit pull origin master\ngit stash "
},
{
"path": "dev-update-travis",
"chars": 108,
"preview": "echo \"Building the patcher\"\ngo generate\n./update-deps-linux\ngo build -ldflags=\"-s -w\" -o Patcher \"./patcher\""
},
{
"path": "dev-update.bat",
"chars": 463,
"preview": "@echo off\n\necho Updating the dependencies\ngo get\nif %errorlevel% neq 0 (\n\tpause\n\texit /b %errorlevel%\n)\n\ngo get -u githu"
},
{
"path": "docs/configuration.md",
"chars": 12145,
"preview": "# Configuration\n\nFor configuring the system, Gosora has a file called `config/config.json` which you can tweak to change"
},
{
"path": "docs/custom_pages.md",
"chars": 564,
"preview": "# Custom Pages\n\nThere are two ways to create custom pages in Gosora, one which requires a lot more technical knowledge t"
},
{
"path": "docs/emoji.md",
"chars": 1105,
"preview": "# Emoji\n\nEmojis are a work in progress. We plan to implement UIs to input and pick them easily in the future. We also pl"
},
{
"path": "docs/installation.md",
"chars": 7119,
"preview": "# Windows Installation\n\nRun `install.bat`, e.g. double-click on it. You will also have to start-up MySQL, which if you'r"
},
{
"path": "docs/internationalisation.md",
"chars": 1988,
"preview": "# Internationalisation\n\nInternationalisation is one of Gosora's top priorities, although not the only one. This means ma"
},
{
"path": "docs/landing_page.md",
"chars": 207,
"preview": "# Landing Page\n\nYou can change the landing page of your site (in other words, the page the user lands on by default, aka"
},
{
"path": "docs/templates.md",
"chars": 910,
"preview": "# Templates\n\nGosora uses a subset of [Go Templates](https://golang.org/pkg/text/template/) which are run on both the ser"
},
{
"path": "docs/updating.md",
"chars": 3811,
"preview": "# Updating Gosora (Windows)\n\nThe update system is currently under development, but you can run `dev-update.bat` to updat"
},
{
"path": "docs/weak_passwords.md",
"chars": 918,
"preview": "# Weak Passwords\n\nFor configuring the list of weak passwords and weak password detection rules, we have `config/weakpass"
},
{
"path": "experimental/config.json",
"chars": 530,
"preview": "{\n\t\"config.DefaultGroup\": 3,\n\t\"config.ActivationGroup\": 5,\n\t\"staff_css\": \" background-color: #ffeaff;\",\n\t\"uncategorised_"
},
{
"path": "experimental/counterTree/tree.go",
"chars": 1774,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/bits\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n)\n\nconst debug = true\n\ntype TreeCounterNode struct {"
},
{
"path": "experimental/counterTree/tree_test.go",
"chars": 346,
"preview": "package main\n\nimport (\n\t\"log\"\n\t\"testing\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tcounter := newTreeTopicViewCounter()\n\tcoun"
},
{
"path": "experimental/module_lua.go",
"chars": 50,
"preview": "/* Copyright Azareal 2016 - 2017 */\npackage main\n\n"
},
{
"path": "experimental/module_v8js.go",
"chars": 50,
"preview": "/* Copyright Azareal 2016 - 2017 */\npackage main\n\n"
},
{
"path": "experimental/new-replybit.html",
"chars": 1586,
"preview": "<div class=\"rowitem passive deletable_block editable_parent post_item\" style=\"background-color: #eaeaea;padding-top: 3px"
},
{
"path": "experimental/new-update.bat",
"chars": 246,
"preview": "@echo off\n\necho Updating the dependencies\ngo get\nif %errorlevel% neq 0 (\n\tpause\n\texit /b %errorlevel%\n)\n\necho Building t"
},
{
"path": "experimental/plugin_geoip.go",
"chars": 469,
"preview": "package main\n\nimport c \"github.com/Azareal/Gosora/common\"\nimport \"github.com/oschwald/geoip2-golang\"\n\nvar geoipDB *geoip"
},
{
"path": "experimental/plugin_sendmail.go",
"chars": 1879,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"os/exec\"\n\t\"runtime\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n)\n\n/*\n\tSending email"
},
{
"path": "experimental/theme-ext.json",
"chars": 326,
"preview": "{\n\t\"Name\": \"tempra-simple\",\n\t\"FriendlyName\": \"Tempra Simple\",\n\t\"Version\": \"0.0.1\",\n\t\"Creator\": \"Azareal\",\n\t\"Settings\": {"
},
{
"path": "experimental/theme-ext.xml",
"chars": 474,
"preview": "<?xml version=\"1.0\" ?>\n<theme>\n\t<name>tempra-simple</name>\n\t<friendlyName>Tempra Simple</friendlyName>\n\t<version>0.0.1</"
},
{
"path": "extend/adventure/lib/adventure.go",
"chars": 461,
"preview": "package adventure\n\n// We're experimenting with struct tags here atm\ntype Adventure struct {\n\tID int `schema:\"n"
},
{
"path": "extend/adventure/lib/adventure_store.go",
"chars": 115,
"preview": "package adventure\n\ntype AdventureStore interface {\n\tCreate() (int, error)\n}\n\ntype DefaultAdventureStore struct {\n}\n"
},
{
"path": "extend/adventure/plugin.json",
"chars": 124,
"preview": "{\n\t\"UName\":\"adventure\",\n\t\"Name\":\"Adventure\",\n\t\"Author\":\"Azareal\",\n\t\"URL\":\"https://github.com/Azareal/Gosora\",\n\t\"Skip\":tr"
},
{
"path": "extend/adventure/prebuild/filler.txt",
"chars": 73,
"preview": "This file is here so that Git will include this folder in the repository."
},
{
"path": "extend/filler.go",
"chars": 14,
"preview": "package extend"
},
{
"path": "extend/guilds/lib/guild_store.go",
"chars": 1453,
"preview": "package guilds\n\nimport (\n\t\"database/sql\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar Gstore GuildStore\n\ntype Gui"
},
{
"path": "extend/guilds/lib/guilds.go",
"chars": 14184,
"preview": "package guilds // import \"github.com/Azareal/Gosora/extend/guilds/lib\"\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"e"
},
{
"path": "extend/guilds/plugin.json",
"chars": 118,
"preview": "{\n\t\"UName\":\"guilds\",\n\t\"Name\":\"Guilds\",\n\t\"Author\":\"Azareal\",\n\t\"URL\":\"https://github.com/Azareal/Gosora\",\n\t\"Skip\":true\n}"
},
{
"path": "extend/guilds/plugin_guilds.go",
"chars": 5599,
"preview": "package main\n\nimport (\n\tc \"github.com/Azareal/Gosora/common\"\n\tguilds \"github.com/Azareal/Gosora/extend/guilds/lib\"\n)\n\n//"
},
{
"path": "extend/guilds/prebuild/filler.txt",
"chars": 73,
"preview": "This file is here so that Git will include this folder in the repository."
},
{
"path": "extend/heytherejs/main.js",
"chars": 85,
"preview": "current_page.test = true;\n\n// This shouldn't ever fail\nvar errmsg = \"gotcha\";\nerrmsg;"
},
{
"path": "extend/heytherejs/plugin.json",
"chars": 131,
"preview": "{\n\t\"UName\":\"heytherejs\",\n\t\"Name\":\"HeythereJS\",\n\t\"Author\":\"Azareal\",\n\t\"URL\":\"https://github.com/Azareal/Gosora\",\n\t\"Main\":"
},
{
"path": "extend/plugin_adventure.go",
"chars": 689,
"preview": "// WIP - Experimental adventure plugin, this might find a new home soon, but it's here to stress test Gosora's extensibi"
},
{
"path": "extend/plugin_bbcode.go",
"chars": 11867,
"preview": "package extend\n\nimport (\n\t\"bytes\"\n\t\"math/rand\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"time\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n)\n\nva"
},
{
"path": "extend/plugin_heythere.go",
"chars": 867,
"preview": "package extend\n\nimport c \"github.com/Azareal/Gosora/common\"\n\nfunc init() {\n\tc.Plugins.Add(&c.Plugin{UName: \"heythere\", N"
},
{
"path": "extend/plugin_hyperdrive.go",
"chars": 6739,
"preview": "// Highly experimental plugin for caching rendered pages for guests\npackage extend\n\nimport (\n\t//\"log\"\n\t\"bytes\"\n\t\"errors\""
},
{
"path": "extend/plugin_markdown.go",
"chars": 9172,
"preview": "package extend\n\nimport (\n\t\"strings\"\n\n\tc \"github.com/Azareal/Gosora/common\"\n)\n\nvar markdownMaxDepth = 25 // How deep the "
},
{
"path": "extend/plugin_skeleton.go",
"chars": 2376,
"preview": "package extend\n\nimport c \"github.com/Azareal/Gosora/common\"\n\nfunc init() {\n\t/*\n\t\tThe UName field should match the name i"
},
{
"path": "gen_mssql.go",
"chars": 4299,
"preview": "// +build mssql\n\n// This file was generated by Gosora's Query Generator. Please try to avoid modifying this file, as it "
},
{
"path": "gen_mysql.go",
"chars": 3166,
"preview": "// +build !pgsql,!mssql\n\n/* This file was generated by Gosora's Query Generator. Please try to avoid modifying this file"
},
{
"path": "gen_pgsql.go",
"chars": 1580,
"preview": "// +build pgsql\n\n// This file was generated by Gosora's Query Generator. Please try to avoid modifying this file, as it "
},
{
"path": "gen_router.go",
"chars": 81334,
"preview": "// Code generated by Gosora's Router Generator. DO NOT EDIT.\n/* This file was automatically generated by the software. P"
},
{
"path": "gen_tables.go",
"chars": 649,
"preview": "// Generated by Gosora's Query Generator. DO NOT EDIT.\npackage main\n\nvar dbTablePrimaryKeys = map[string]string{\n\t\"polls"
},
{
"path": "general_test.go",
"chars": 51093,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"database/sql\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"st"
},
{
"path": "go.mod",
"chars": 1168,
"preview": "module github.com/Azareal/Gosora\n\nrequire (\n\tcloud.google.com/go v0.31.0 // indirect\n\tgithub.com/Azareal/gopsutil v0.0.0"
},
{
"path": "go.sum",
"chars": 12739,
"preview": "cloud.google.com/go v0.31.0 h1:o9K5MWWt2wk+d9jkGn2DAZ7Q9nUdnFLOpK9eIkDwONQ=\ncloud.google.com/go v0.31.0/go.mod h1:aQUYkX"
},
{
"path": "gosora_example.service",
"chars": 428,
"preview": "# An example systemd service file\n[Unit]\nDescription=Gosora\n\n[Service]\nUser=gosora\nGroup=www-data\n\nRestart=on-failure\nRe"
},
{
"path": "install/install.go",
"chars": 1160,
"preview": "package install\n\nimport (\n\t\"fmt\"\n\n\tqgen \"github.com/Azareal/Gosora/query_gen\"\n)\n\nvar adapters = make(map[string]InstallA"
},
{
"path": "install/mssql.go",
"chars": 3764,
"preview": "/*\n*\n* Gosora MSSQL Interface\n* Copyright Azareal 2017 - 2018\n*\n */\npackage install\n\nimport (\n\t\"bytes\"\n\t\"database/sql\"\n\t"
},
{
"path": "install/mysql.go",
"chars": 5067,
"preview": "/*\n*\n* Gosora MySQL Interface\n* Copyright Azareal 2017 - 2020\n*\n */\npackage install\n\nimport (\n\t\"bytes\"\n\t\"database/sql\"\n\t"
},
{
"path": "install/pgsql.go",
"chars": 2530,
"preview": "/*\n*\n* Gosora PostgreSQL Interface\n* Under heavy development\n* Copyright Azareal 2017 - 2019\n*\n */\npackage install\n\nimpo"
},
{
"path": "install/utils.go",
"chars": 740,
"preview": "package install\n\nimport \"encoding/base64\"\nimport \"crypto/rand\"\nimport \"golang.org/x/crypto/bcrypt\"\n\nconst saltLength int"
},
{
"path": "install-docker",
"chars": 385,
"preview": "go get -u github.com/mailru/easyjson/...\neasyjson -pkg common\ngo get\n\ngo build -ldflags=\"-s -w\" -o Installer \"./cmd/inst"
},
{
"path": "install-linux",
"chars": 183,
"preview": "echo \"Installing the dependencies\"\n./update-deps-linux\n\necho \"Building the installer\"\ngo build -ldflags=\"-s -w\" -o Insta"
},
{
"path": "install.bat",
"chars": 430,
"preview": "@echo off\n\necho Installing the dependencies\ngo get -u github.com/mailru/easyjson/...\nif %errorlevel% neq 0 (\n\tpause\n\texi"
},
{
"path": "langs/english.json",
"chars": 55947,
"preview": "{\n\t\"Name\": \"english\",\n\t\"IsoCode\":\"en\",\n\t\n\t\"Levels\": {\n\t\t\"Level\": \"<span class='level_hideable'>Level </span>{0}\",\n\t\t\"Lev"
}
]
// ... and 495 more files (download for full content)
About this extraction
This page contains the full source code of the Azareal/Gosora GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 695 files (2.9 MB), approximately 801.9k tokens, and a symbol index with 3142 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.