├── .env ├── .gitignore ├── .gitmodules ├── .npmignore ├── .vscode └── settings.json ├── .yarnclean ├── CONTRIBUTING.adoc ├── LICENSE.txt ├── Makefile ├── NOTICE.txt ├── README.md ├── appsv ├── model │ ├── .gitignore │ ├── COPYRIGHT.txt │ ├── LICENSE-AGPL-3.0.txt │ ├── build.sbt │ ├── docs │ │ └── notes.txt │ ├── lock.sbt │ ├── old │ │ ├── .build-log.txt │ │ ├── nice.debate.xhtml │ │ └── notes-old.txt │ └── src │ │ ├── main │ │ ├── java │ │ │ └── name │ │ │ │ └── fraser │ │ │ │ └── neil │ │ │ │ └── plaintext │ │ │ │ └── diff_match_patch.java │ │ └── scala │ │ │ └── com │ │ │ └── debiki │ │ │ └── core │ │ │ ├── AuditLogEntry.scala │ │ │ ├── Category.scala │ │ │ ├── CompletedFormRenderer.scala │ │ │ ├── DbDao2.scala │ │ │ ├── EditedSettings.scala │ │ │ ├── Email.scala │ │ │ ├── Event.scala │ │ │ ├── IdentityProvider.scala │ │ │ ├── Notice.scala │ │ │ ├── Page.scala │ │ │ ├── PageParts.scala │ │ │ ├── PagePath.scala │ │ │ ├── PagePopularityStats.scala │ │ │ ├── PinnedPositionCalcer.scala │ │ │ ├── Post.scala │ │ │ ├── PostAction.scala │ │ │ ├── PostRelType.scala │ │ │ ├── PostRevision.scala │ │ │ ├── PostsReadStats.scala │ │ │ ├── Prelude.scala │ │ │ ├── ReviewReason.scala │ │ │ ├── ReviewTask.scala │ │ │ ├── ScalaBasedDatabaseMigrations.scala │ │ │ ├── Site.scala │ │ │ ├── SiteTransaction.scala │ │ │ ├── SystemTransaction.scala │ │ │ ├── Tag.scala │ │ │ ├── TrueId.scala │ │ │ ├── TySession.scala │ │ │ ├── Validation.scala │ │ │ ├── dao-db.scala │ │ │ ├── exceptions.scala │ │ │ ├── links.scala │ │ │ ├── notifications.scala │ │ │ ├── package.scala │ │ │ ├── permissions.scala │ │ │ ├── quota.scala │ │ │ ├── statistics.scala │ │ │ ├── trust-threat-level.scala │ │ │ ├── types.scala │ │ │ ├── uploads.scala │ │ │ ├── user.scala │ │ │ ├── watchbar.scala │ │ │ └── webhooks.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── debiki │ │ └── core │ │ ├── PagePartsTest.scala │ │ ├── PagePathTest.scala │ │ ├── PageReadingProgressTest.scala │ │ ├── PinnedPositionCalcerSpec.scala │ │ ├── PreludeTest.scala │ │ ├── QuotaTest.scala │ │ ├── ReviewTaskSpec.scala │ │ ├── StatsCalcTest.scala │ │ ├── StringInterpolatorsTest.scala │ │ ├── UserSpec.scala │ │ ├── ValidationTest.scala │ │ └── WatchbarTest.scala ├── rdb │ ├── .gitignore │ ├── build.sbt │ ├── docs │ │ └── notes.txt │ ├── lock.sbt │ ├── scripts │ │ ├── create-play_evolutions-table.sql │ │ ├── debiki-pg-views.sql │ │ ├── debiki-postgre.sql │ │ ├── delete-tenant.txt │ │ ├── old │ │ │ ├── flyway-migrations │ │ │ │ ├── y2014 │ │ │ │ │ ├── v1__base_version.sql │ │ │ │ │ ├── v2__posts_read_stats.sql │ │ │ │ │ ├── v3__no_email.sql │ │ │ │ │ ├── v4__drop_login_table_add_username.sql │ │ │ │ │ ├── v5__multireply.sql │ │ │ │ │ ├── v6__always_commonmark.sql │ │ │ │ │ ├── v7__user_page_settings.sql │ │ │ │ │ ├── v8__email_for_each_new_post.sql │ │ │ │ │ └── v9__page_role_homepage.sql │ │ │ │ ├── y2015 │ │ │ │ │ ├── v10__create_main_site.sql │ │ │ │ │ ├── v11__site_creator_email.sql │ │ │ │ │ ├── v12__simple_quotas.sql │ │ │ │ │ ├── v13__dw2_posts.sql │ │ │ │ │ ├── v14__migrate_posts.scala │ │ │ │ │ ├── v15__constraints_triggers.sql │ │ │ │ │ ├── v16__merge_guests_users.sql │ │ │ │ │ ├── v17__invites.sql │ │ │ │ │ ├── v18__block_bury_https_scrypt.sql │ │ │ │ │ ├── v19__last_reply_bugfix_plans.sql │ │ │ │ │ ├── v20__target_site_id.sql │ │ │ │ │ ├── v21__pinned_topics_about_page_post_type.sql │ │ │ │ │ ├── v22__q_a_site.sql │ │ │ │ │ ├── v23__problems_ideas.sql │ │ │ │ │ ├── v24__categories.sql │ │ │ │ │ ├── v25__cache_html.sql │ │ │ │ │ ├── v26__longer_site_hostname.sql │ │ │ │ │ ├── v27__longer_emails.sql │ │ │ │ │ ├── v28__uploads.sql │ │ │ │ │ ├── v29__post_revisions.sql │ │ │ │ │ ├── v30__avatars.sql │ │ │ │ │ ├── v31__review.sql │ │ │ │ │ ├── v32__upload_quotas.sql │ │ │ │ │ ├── v33__messages.sql │ │ │ │ │ └── v34__notfs_index.sql │ │ │ │ └── y2016 │ │ │ │ │ ├── v35__html_tag_css_classes.sql │ │ │ │ │ ├── v36__safe_settings.sql │ │ │ │ │ ├── v37__audit_log_email.sql │ │ │ │ │ └── v38__no_deleted_by.sql │ │ │ └── play-evolutions │ │ │ │ ├── 1.sql │ │ │ │ ├── 10.sql │ │ │ │ ├── 11.sql │ │ │ │ ├── 12.sql │ │ │ │ ├── 13.sql │ │ │ │ ├── 14.sql │ │ │ │ ├── 15.sql │ │ │ │ ├── 16.sql │ │ │ │ ├── 2.sql │ │ │ │ ├── 3.sql │ │ │ │ ├── 4.sql │ │ │ │ ├── 5.sql │ │ │ │ ├── 6.sql │ │ │ │ ├── 7.sql │ │ │ │ ├── 8.sql │ │ │ │ └── 9.sql │ │ ├── pending-evolutions.sql │ │ └── useful-commands.sql │ └── src │ │ └── main │ │ ├── resources │ │ └── db │ │ │ └── migration │ │ │ ├── db-wip.sql │ │ │ ├── old │ │ │ └── skip_v426__bookmarks.sql │ │ │ ├── r__comments.sql │ │ │ ├── r__functions.sql │ │ │ ├── r__triggers.sql │ │ │ ├── tags-old-wip.sql │ │ │ ├── wip_r__views.sql │ │ │ ├── y2016 │ │ │ ├── v10__remove_constraint.sql │ │ │ ├── v11__topic_list_style.sql │ │ │ ├── v12__spam_settings.sql │ │ │ ├── v13__page_hidden_at.sql │ │ │ ├── v14__more_settings.sql │ │ │ ├── v15__change_usernamed.sql │ │ │ ├── v16__show_settings.sql │ │ │ ├── v17__check_names_trimmed.sql │ │ │ ├── v1__base_version.sql │ │ │ ├── v2__rename_tables.sql │ │ │ ├── v3__unknown_user.sql │ │ │ ├── v4__index_queue.sql │ │ │ ├── v5__superadmin.sql │ │ │ ├── v6__branch_sideways.sql │ │ │ ├── v7__tags.sql │ │ │ ├── v8__system_user_id_1.sql │ │ │ ├── v9__spam_check_queue.sql │ │ │ └── wip.sql │ │ │ ├── y2017 │ │ │ ├── v18__forum_settings_user_stats.sql │ │ │ ├── v19__site_id_int_perms_on_pages.sql │ │ │ ├── v20__migr_perms.sql │ │ │ ├── v21__username_lowercase.sql │ │ │ ├── v22__backup_test_log.sql │ │ │ ├── v23__no_email.sql │ │ │ ├── v24__page_pop.sql │ │ │ ├── v25__summary_emails.sql │ │ │ ├── v26__nulls_last.sql │ │ │ ├── v27__plan_done_any_role.sql │ │ │ ├── v28__allow_embedding.sql │ │ │ ├── v29__remove_columns.sql │ │ │ ├── v30__page_started_at_wait_until.sql │ │ │ ├── v31__num_posts_total.sql │ │ │ └── v32__page_constraints.sql │ │ │ ├── y2018 │ │ │ ├── v33__many_emails.sql │ │ │ ├── v34__longer_allow_embedding.sql │ │ │ ├── v35__sub_communities_lang_delete_user.sql │ │ │ ├── v367__more_settings.sql │ │ │ ├── v368__no_browser_id_cookie.sql │ │ │ ├── v369__cache_many_undo_review.sql │ │ │ ├── v36__publ_site_id.sql │ │ │ ├── v370__bump_after_close.sql │ │ │ ├── v371__drafts.sql │ │ │ ├── v372__sso_and_api.sql │ │ │ ├── v373__sso_settings.sql │ │ │ ├── v374__many_invites.sql │ │ │ ├── v375__more_notfs.sql │ │ │ └── v376__guest_id_constr.sql │ │ │ ├── y2019 │ │ │ ├── v377__tour_tips_seen.sql │ │ │ ├── v378__more_settings.sql │ │ │ ├── v379__tos_privacy_settings.sql │ │ │ ├── v380__review_task_notfs.sql │ │ │ ├── v381__spam_check_results.sql │ │ │ ├── v382__group_members.sql │ │ │ ├── v383__ext_imp_id.sql │ │ │ ├── v384__invites_add_to_group.sql │ │ │ ├── v385__sso_logout_url.sql │ │ │ ├── v386__guests_wo_browserid_use_extid.sql │ │ │ ├── v387__email_length.sql │ │ │ └── v388__rm_email_notf_char_check.sql │ │ │ ├── y2020 │ │ │ ├── v389__backup_test_log.sql │ │ │ ├── v390__sort_order.sql │ │ │ ├── v391__root_cat_slug.sql │ │ │ ├── v392__cors_settings.sql │ │ │ ├── v393__nav_conf_settings.sql │ │ │ ├── v394__settings.sql │ │ │ ├── v395__appr_bef_settings.sql │ │ │ ├── v396__links.sql │ │ │ ├── v397__oidc.sql │ │ │ ├── v398__max_html_length.sql │ │ │ ├── v399__site_feature_flags.sql │ │ │ ├── v400__missing_fx_ix.sql │ │ │ ├── v401__sort_and_media_settings.sql │ │ │ └── v402__idp_verif_email_domains.sql │ │ │ ├── y2021 │ │ │ ├── v403__settings_purge_sites.sql │ │ │ ├── v404__profile_pic_length.sql │ │ │ ├── v405__email_secrets.sql │ │ │ ├── v406__emb_coms_sso.sql │ │ │ ├── v407__upvote_features.adoc │ │ │ ├── v407__upvote_features.sql │ │ │ ├── v408__email_from_name.sql │ │ │ ├── v409__notices.sql │ │ │ ├── v410__tags_refactored.sql │ │ │ ├── v411__sessions_t.sql │ │ │ └── v412__forum_search_box.sql │ │ │ ├── y2022 │ │ │ ├── v413__webhooks.sql │ │ │ ├── v414__webhooks_constr.sql │ │ │ ├── v415__drop_reply_at_constr.sql │ │ │ ├── v416__may_and_msg_id.sql │ │ │ ├── v417__smtp_id_pat_rels.sql │ │ │ └── v418__comment_sort_deindex.sql │ │ │ ├── y2023 │ │ │ ├── v419__anon_posts_disc_prefs.sql │ │ │ ├── v420__ugc_domain_sort_ideas.sql │ │ │ ├── v421__sys_settings.sql │ │ │ ├── v422__maint_msg.sql │ │ │ ├── v423__type_val_type.sql │ │ │ └── v424__job_queue.sql │ │ │ ├── y2024 │ │ │ ├── v425__term_sess_more_conf.sql │ │ │ ├── v426__bookmarks.sql │ │ │ └── wip_v427__alias.sql │ │ │ ├── y2025 │ │ │ └── v427__switch_to_sqlx.sql │ │ │ └── y2999 │ │ │ ├── wip_sect_disc_props_views_stats.sql │ │ │ ├── wip_v41Y__samesite_none.sql │ │ │ └── wip_v427__sidebar_menu.sql │ │ └── scala │ │ ├── com │ │ └── debiki │ │ │ └── dao │ │ │ └── rdb │ │ │ ├── ApiSecretsSiteDaoMixin.scala │ │ │ ├── AuditLogSiteDaoMixin.scala │ │ │ ├── AuthnSiteTxMixin.scala │ │ │ ├── BlocksSiteDaoMixin.scala │ │ │ ├── CategoriesSiteDaoMixin.scala │ │ │ ├── CreateSiteSystemDaoMixin.scala │ │ │ ├── DraftsSiteDaoMixin.scala │ │ │ ├── EmailAddressesSiteDaoMixin.scala │ │ │ ├── LinksSiteTxMixin.scala │ │ │ ├── LoginSiteDaoMixin.scala │ │ │ ├── NotificationsSiteDaoMixin.scala │ │ │ ├── PageNotfPrefsSiteTxMixin.scala │ │ │ ├── PageUsersSiteDaoMixin.scala │ │ │ ├── PagesSiteDaoMixin.scala │ │ │ ├── PermsOnPagesRdbMixin.scala │ │ │ ├── PostsReadStatsSiteDaoMixin.scala │ │ │ ├── PostsSiteDaoMixin.scala │ │ │ ├── Rdb.scala │ │ │ ├── RdbDaoFactory.scala │ │ │ ├── RdbSiteTransaction.scala │ │ │ ├── RdbSystemTransaction.scala │ │ │ ├── RdbUtil.scala │ │ │ ├── ReviewTasksSiteDaoMixin.scala │ │ │ ├── SearchSiteDaoMixin.scala │ │ │ ├── SessionsRdbMixin.scala │ │ │ ├── SettingsSiteDaoMixin.scala │ │ │ ├── SpamCheckQueueDaoMixin.scala │ │ │ ├── TagsRdbMixin.scala │ │ │ ├── TagsSiteDaoMixin.scala │ │ │ ├── UploadsSiteDaoMixin.scala │ │ │ ├── UserSiteDaoMixin.scala │ │ │ ├── UsernamesSiteDaoMixin.scala │ │ │ └── WebhooksRdbMixin.scala │ │ └── db │ │ └── migration │ │ └── MigrationHelper.scala └── server │ ├── controllers │ ├── AdminController.scala │ ├── ApiSecretsController.scala │ ├── ApiV0Controller.scala │ ├── Application.scala │ ├── CloseCollapseController.scala │ ├── CreateSiteController.scala │ ├── CustomFormController.scala │ ├── DebugTestController.scala │ ├── DraftsController.scala │ ├── EditController.scala │ ├── EmbeddedTopicsController.scala │ ├── FlagController.scala │ ├── ForumController.scala │ ├── GroupTalkController.scala │ ├── ImpersonateController.scala │ ├── InviteController.scala │ ├── LegalController.scala │ ├── LoginAsGuestController.scala │ ├── LoginController.scala │ ├── LoginWithOpenAuthController.scala │ ├── LoginWithPasswordController.scala │ ├── ModerationController.scala │ ├── PageController.scala │ ├── PageTitleSettingsController.scala │ ├── ReplyController.scala │ ├── ResetPasswordController.scala │ ├── SearchController.scala │ ├── SettingsController.scala │ ├── SiteAssetBundlesController.scala │ ├── SpecialContentController.scala │ ├── SuperAdminController.scala │ ├── TagsController.scala │ ├── UnsubscriptionController.scala │ ├── UploadsController.scala │ ├── UserController.scala │ ├── Utils.scala │ ├── ViewPageController.scala │ ├── VoteController.scala │ └── package.scala │ ├── debiki │ ├── AssetBundleLoader.scala │ ├── DatabaseUtils.scala │ ├── DeadlockDetector.scala │ ├── Debiki.scala │ ├── DebikiHttp.scala │ ├── Globals.scala │ ├── HtmlUtils.scala │ ├── ImageUtils.scala │ ├── JsonUtils.scala │ ├── MailerActor.scala │ ├── MostMetrics.scala │ ├── Nashorn.scala │ ├── PageTpi.scala │ ├── RateLimiter.scala │ ├── RateLimits.scala │ ├── ReactJson.scala │ ├── SpecialContentPages.scala │ ├── TextAndHtml.scala │ ├── ThingsToReview.scala │ ├── dao │ │ ├── AssetBundleDao.scala │ │ ├── AuditDao.scala │ │ ├── CategoriesDao.scala │ │ ├── CreateSiteDao.scala │ │ ├── FeedsDao.scala │ │ ├── ForumDao.scala │ │ ├── MemCache.scala │ │ ├── MessagesDao.scala │ │ ├── PageDao.scala │ │ ├── PageLinksDao.scala │ │ ├── PagePathMetaDao.scala │ │ ├── PageStuffDao.scala │ │ ├── PagesDao.scala │ │ ├── PostsDao.scala │ │ ├── RedisCache.scala │ │ ├── RenderContentService.scala │ │ ├── RenderedPageHtmlDao.scala │ │ ├── ReviewsDao.scala │ │ ├── SearchDao.scala │ │ ├── SettingsDao.scala │ │ ├── SiteDao.scala │ │ ├── SpecialContentDao.scala │ │ ├── SystemDao.scala │ │ ├── TagsDao.scala │ │ ├── UploadsDao.scala │ │ ├── UserDao.scala │ │ ├── WatchbarDao.scala │ │ └── package.scala │ ├── feed-serializers.scala │ ├── package.scala │ └── settings.scala │ ├── ed │ ├── server │ │ └── EdAppLoader.scala │ └── stackdriver │ │ └── StackdriverLayout.scala │ ├── talkyard │ └── server │ │ ├── JsX.scala │ │ ├── PostRenderer.scala │ │ ├── TyAppLoader.scala │ │ ├── TyController.scala │ │ ├── TyFilters.scala │ │ ├── api │ │ ├── ActionDoer.scala │ │ ├── ActionParser.scala │ │ ├── GetController.scala │ │ ├── GetPatsImpl.scala │ │ ├── InclFields.scala │ │ ├── InclFieldsParSer.scala │ │ ├── ListController.scala │ │ ├── PostsListFoundJson.scala │ │ ├── QueryDoController.scala │ │ ├── ThingsFoundJson.scala │ │ └── package.scala │ │ ├── authn │ │ ├── AuthnSiteDaoMixin.scala │ │ ├── OAuth2Provider-from-Silhouette.scala │ │ ├── OidcClaims.scala │ │ ├── SsoAuthnController.scala │ │ ├── TyOidcScribeJavaApi20.scala │ │ ├── WellKnownIdps.scala │ │ └── package.scala │ │ ├── authz │ │ ├── Authz.scala │ │ ├── AuthzSiteDaoMixin.scala │ │ ├── ReqrAndTgt.scala │ │ └── package.scala │ │ ├── dao │ │ ├── StaleStuff.scala │ │ └── package.scala │ │ ├── emails │ │ ├── in │ │ │ ├── EmailsInController.scala │ │ │ └── package.scala │ │ ├── out │ │ │ ├── Emails.scala │ │ │ └── package.scala │ │ └── transl │ │ ├── events │ │ ├── EventsParSer.scala │ │ ├── WebhooksController.scala │ │ ├── WebhooksParSer.scala │ │ └── WebhooksSiteDaoMixin.scala │ │ ├── http │ │ ├── DebikiRequest.scala │ │ ├── JsonOrFormRequestBody.scala │ │ ├── PageRequest.scala │ │ ├── PlainApiActions.scala │ │ ├── SafeActions.scala │ │ └── package.scala │ │ ├── jobs │ │ └── Janitor.scala │ │ ├── linkpreviews │ │ ├── LinkPreviewHtml.scala │ │ ├── LinkPreviewRenderer.scala │ │ └── engines │ │ │ ├── GiphyOnebox.scala │ │ │ ├── ImageOnebox.scala │ │ │ ├── OEmbedLinkPrevwRendrEng.scala │ │ │ ├── VideoOnebox.scala │ │ │ ├── YouTubeOnebox.scala │ │ │ └── linkPreviewEngines.scala │ │ ├── logging │ │ ├── LastErrsActor.scala │ │ └── package.scala │ │ ├── migrations │ │ ├── Migration14.scala │ │ └── ScalaBasedMigrations.scala │ │ ├── notf │ │ ├── NotfHtmlRenderer.scala │ │ ├── NotificationGenerator.scala │ │ └── NotifierActor.scala │ │ ├── package.scala │ │ ├── parser │ │ ├── MapParSer.scala │ │ ├── PageParSer.scala │ │ ├── PasetoParSer.scala │ │ ├── ScalarsParSer.scala │ │ └── package.scala │ │ ├── plugins │ │ └── utx │ │ │ └── UsabilityTestingExchangeController.scala │ │ ├── pop │ │ ├── PagePopularityCalculator.scala │ │ └── PagePopularityDao.scala │ │ ├── pubsub │ │ ├── PubSub.scala │ │ ├── SubscriberController.scala │ │ └── WebSocketMessageHandler.scala │ │ ├── rendr │ │ └── package.scala │ │ ├── search │ │ ├── IndexCreator.scala │ │ ├── SearchEnginIndexer.scala │ │ ├── SearchEngine.scala │ │ ├── SearchQuery.scala │ │ ├── SearchQueryParser.scala │ │ └── package.scala │ │ ├── security │ │ ├── PasetoSec.scala │ │ ├── ReservedNames.scala │ │ ├── WhatApiSecret.scala │ │ └── package.scala │ │ ├── sess │ │ ├── SessionController.scala │ │ └── SessionSiteDaoMixin.scala │ │ ├── sitepatch │ │ ├── SitePatch.scala │ │ ├── SitePatchController.scala │ │ ├── SitePatchMaker.scala │ │ ├── SitePatchParser.scala │ │ └── SitePatcher.scala │ │ ├── spam │ │ ├── QuickSpamCheckDao.scala │ │ ├── SpamCheckActor.scala │ │ └── SpamChecker.scala │ │ ├── summaryemails │ │ ├── SummaryEmailsDao.scala │ │ └── UnsubFromSummariesController.scala │ │ ├── talk │ │ └── PostsController.scala │ │ └── util │ │ └── package.scala │ └── views │ ├── adminPage.scala.html │ ├── adminlogin │ ├── adminLoginPage.scala.html │ └── oneTimeLoginLinkEmail.scala.html │ ├── authn │ ├── authnPage.scala.html │ ├── authnPageHtmlDoc.scala.html │ ├── sendAuthnResultToOpenerCloseCurWin.scala.html │ ├── showCreateUserDialogInOpenerCloseCurWin.scala.html │ └── showCreateUserDialogInThisWin.scala.html │ ├── confirmOneMoreEmailAddressEmail.scala.html │ ├── createaccount │ ├── accountAlreadyExistsEmail.scala.html │ ├── accountApprovedEmail.scala.html │ ├── createAccountLinkEmail.scala.html │ ├── newMemberToApproveEmail.scala.html │ └── welcomePage.scala.html │ ├── createsite │ ├── createSitePage.scala.html │ ├── main.scala.html │ └── welcomeEmail.scala.html │ ├── debikiMeta.scala.html │ ├── debikiScriptsHead.scala.html │ ├── debikiStyles.scala.html │ ├── editorHelp.scala.html │ ├── emailVerified.scala.html │ ├── embeddedEditor.scala.html │ ├── googleAnalytics.scala.html │ ├── invite │ ├── inviteAcceptedEmail.scala.html │ ├── inviteEmail.scala.html │ └── welcomeSetPasswordEmail.scala.html │ ├── legal │ ├── privacyPolicy.scala.html │ └── termsOfUse.scala.html │ ├── login │ ├── accountsLinkedPleaseLoginAgain.scala.html │ ├── accountsNotLinkedPleaseLoginAgain.scala.html │ ├── askIfLinkAccounts.scala.html │ ├── verifyYourEmailAddr.scala.html │ └── verifyYourEmailAddrEmail.scala.html │ ├── resetpassword │ ├── chooseNewPassword.scala.html │ ├── emailSent.scala.html │ ├── passwordHasBeenChanged.scala.html │ ├── resetPasswordEmail.scala.html │ └── specifyEmailAddress.scala.html │ ├── scripts │ └── googleAnalytics4.scala.html │ ├── specialpages │ └── createSomethingHerePage.scala.html │ ├── summaryemails │ ├── unsubFromSummariesDonePage.scala.html │ └── unsubFromSummariesPage.scala.html │ ├── tags │ ├── jsonDataMustBeFirst.scala.html │ └── talkyardScriptBundles.scala.html │ ├── templates │ ├── page.scala.html │ ├── search.scala.html │ ├── users.scala.html │ └── wrapper.scala.html │ ├── unsubscribe │ └── youHaveBeenUnsubscribed.scala.html │ └── unsubscribePage.scala.html ├── build.sbt ├── client ├── app-2d │ ├── sidebar │ │ └── minimap.2d.ts │ └── utterscroll │ │ ├── utterscroll-init-tips.js │ │ ├── utterscroll-init-tips.styl │ │ └── utterscroll.js ├── app-editor │ ├── editor-bundle-already-loaded.d.ts │ ├── editor-prelude.editor.ts │ ├── editor │ │ ├── commonmark.editor.ts │ │ ├── editor.editor.ts │ │ ├── editor.styl │ │ ├── formatting-help.editor.ts │ │ ├── formatting-help.styl │ │ ├── link-previews-markdown-it-plugin.editor.ts │ │ └── mentions-markdown-it-plugin.ts │ └── tsconfig.json ├── app-head │ ├── head-bundle.ts │ └── tsconfig.json ├── app-more │ ├── edit-history │ │ ├── edit-history-dialog.more.ts │ │ └── edit-history-dialog.styl │ ├── editor │ │ ├── PageRole.styl │ │ ├── PageRoleDropdown.more.ts │ │ ├── title-editor.more.ts │ │ └── title-editor.styl │ ├── forum │ │ ├── create-category-dialog.more.ts │ │ ├── create-category-dialog.styl │ │ ├── edit-intro-dialog.more.ts │ │ └── edit-intro-dialog.styl │ ├── help │ │ └── help-dialog.more.ts │ ├── login │ │ ├── create-user-dialog.more.ts │ │ ├── create-user.styl │ │ ├── login-dialog.more.ts │ │ ├── login-dialog.styl │ │ ├── login.styl │ │ ├── new-password-input.more.ts │ │ ├── new-password-input.styl │ │ └── new-password-page.more.ts │ ├── more-bundle-already-loaded.d.ts │ ├── more-prelude.more.ts │ ├── morekit │ │ ├── proxy-diag.more.ts │ │ └── proxy-diag.styl │ ├── no-page │ │ ├── no-page.styl │ │ └── non-existing-page.more.ts │ ├── notification │ │ ├── Notification.more.ts │ │ ├── email-notf-prefs.more.ts │ │ ├── notf-prefs-dropdown.more.ts │ │ ├── notf-prefs-dropdown.styl │ │ └── notfs.styl │ ├── page-dialogs │ │ ├── ChangePageModal.more.ts │ │ ├── ChangePageModal.styl │ │ ├── __dialog-template__.ts │ │ ├── about-user-dialog.more.ts │ │ ├── about-user-dialog.styl │ │ ├── add-remove-people-dialogs.more.ts │ │ ├── anons-allowed-diag.more.ts │ │ ├── choose-author-owner.more.ts │ │ ├── delete-post-dialog.more.ts │ │ ├── delete-post-dialog.styl │ │ ├── disc-layout-dialog.more.ts │ │ ├── flag-dialog.more.ts │ │ ├── likes-dialog.more.ts │ │ ├── move-post-dialog.styl │ │ ├── move-posts-dialog.more.ts │ │ ├── never-alwas-diag.more.ts │ │ ├── progress-bar-dialog.more.ts │ │ ├── pseudonyms-allowed-btn.more.ts │ │ ├── see-wrench-dialog.more.ts │ │ ├── see-wrench-dialog.styl │ │ ├── share-dialog.more.ts │ │ ├── share-dialogs.styl │ │ ├── snooze-notfs-dialog.more.ts │ │ ├── snooze-notfs-dialog.styl │ │ ├── tags-dialog.more.ts │ │ ├── tags-dialog.styl │ │ ├── view-as-dialog.more.ts │ │ ├── view-as-dialog.styl │ │ ├── votes-dialog.styl │ │ ├── wikify-dialog.more.ts │ │ └── wikify-dialog.styl │ ├── page-tools │ │ ├── page-tools.more.ts │ │ └── page-tools.styl │ ├── persona │ │ └── persona-info-diag.ts │ ├── react-bootstrap-old │ │ └── Input.more.ts │ ├── search │ │ ├── search-page.more.ts │ │ └── search-results-page.styl │ ├── sub-communities │ │ └── create-join-sub-community.more.ts │ ├── tags │ │ ├── bookmarks-dropdown.more.ts │ │ ├── bookmarks-dropdown.styl │ │ ├── create-tag-dlg.more.ts │ │ ├── tag-dropdown.more.ts │ │ ├── tag-dropdown.styl │ │ ├── tags-app.more.ts │ │ └── tags.more.styl │ ├── topbar │ │ └── my-menu.more.ts │ ├── tsconfig.json │ ├── users │ │ ├── ActivitySummaryEmailsInterval.more.ts │ │ ├── group-members.more.ts │ │ ├── group-members.styl │ │ ├── groups-page.more.ts │ │ ├── groups-page.styl │ │ ├── pat-perms.more.ts │ │ ├── pat-perms.styl │ │ ├── to-dos.more.ts │ │ ├── user-activity.more.ts │ │ ├── user-drafts-etc.more.ts │ │ ├── user-invites.more.ts │ │ ├── user-invites.styl │ │ ├── user-notifications.more.ts │ │ ├── user-preferences.more.ts │ │ ├── user-prefs.styl │ │ ├── user-summary.more.ts │ │ ├── user-tasks.more.ts │ │ ├── users-page.more.ts │ │ └── users-page.styl │ ├── util │ │ ├── EmailInput.more.ts │ │ ├── FullNameInput.more.ts │ │ ├── UsernameInput.more.ts │ │ ├── resizable.more.ts │ │ ├── stupid-dialog.more.ts │ │ ├── stupid-dialog.styl │ │ └── trust-level-dialog.more.ts │ ├── utils │ │ ├── PageUnloadAlerter.more.ts │ │ ├── PatternInput.more.ts │ │ ├── PatternInput.styl │ │ ├── diff-match-patch.more.ts │ │ └── utils.more.ts │ ├── widgets.more.ts │ └── widgets │ │ └── anon-purpose-btn.more.ts ├── app-slim │ ├── BrowserStorage.ts │ ├── ReactActions.ts │ ├── ReactDispatcher.ts │ ├── ReactStore.ts │ ├── Server.ts │ ├── ServerApi.ts │ ├── avatar │ │ ├── AvatarAndName.ts │ │ ├── avatar.styl │ │ └── avatar.ts │ ├── call-start-stuff.js │ ├── constants.ts │ ├── disconnected.styl │ ├── editor-bundle-not-yet-loaded.ts │ ├── editor │ │ ├── CdnLinkifyer.ts │ │ ├── SelectCategoryDropdown.ts │ │ └── onebox.styl │ ├── form │ │ ├── form.styl │ │ └── form.ts │ ├── forum │ │ ├── forum.styl │ │ └── forum.ts │ ├── help │ │ ├── help.styl │ │ ├── help.ts │ │ └── serverAnnouncements.ts │ ├── if-in-iframe.styl │ ├── if-in-iframe.ts │ ├── init-all-react-roots.ts │ ├── keyboard-shortcuts.styl │ ├── keyboard-shortcuts.ts │ ├── link-previews.ts │ ├── links.ts │ ├── login │ │ ├── login-if-needed.ts │ │ └── login-popup.js │ ├── magic-time.ts │ ├── me-getters.ts │ ├── mixins.styl │ ├── model.ts │ ├── more-bundle-not-yet-loaded.ts │ ├── notification │ │ └── notf-prefs-button.ts │ ├── old │ │ ├── actions │ │ │ └── popup-menu-unused.js │ │ ├── arrows │ │ │ └── arrows-svg-unused.js │ │ ├── inline-threads │ │ │ ├── inline-threads-unused.js │ │ │ └── inline-threads-unused.styl │ │ ├── unused-pin.ls │ │ ├── unused-tagdog.js │ │ ├── unused-unread-posts.ls │ │ └── unused.styl │ ├── oop-methods.ts │ ├── page-dialogs │ │ ├── open-share-popup.ts │ │ ├── server-error-dialog.styl │ │ └── server-error-dialog.ts │ ├── page-methods.ts │ ├── page │ │ ├── arrows.styl │ │ ├── arrows.ts │ │ ├── cats-or-home-link.ts │ │ ├── chat.styl │ │ ├── chat.ts │ │ ├── discussion.ts │ │ ├── hacks.ts │ │ ├── layout-threads.2d.js │ │ ├── metabar.styl │ │ ├── metabar.ts │ │ ├── mind-maps.styl │ │ ├── page.styl │ │ ├── page.ts │ │ ├── post-actions.styl │ │ ├── post-actions.ts │ │ ├── posts-read-tracker.ts │ │ ├── posts.styl │ │ ├── resize-threads.2d.js │ │ ├── scroll-buttons.styl │ │ ├── scroll-buttons.ts │ │ ├── social-buttons.styl │ │ ├── social-buttons.ts │ │ └── threads.styl │ ├── personas │ │ ├── PersonaIndicator.ts │ │ └── personas.ts │ ├── plugins.styl │ ├── plugins │ │ └── usability-testing-exchange.styl │ ├── prelude.ts │ ├── print.styl │ ├── privacy.styl │ ├── pubsub │ │ └── subscriptions.ts │ ├── react-elements │ │ └── name-login-btns.ts │ ├── react-flux │ │ ├── dispatcher.ts │ │ └── invariant.ts │ ├── rules.ts │ ├── server-vars.ts │ ├── sidebar │ │ ├── sidebar.styl │ │ └── sidebar.ts │ ├── slim-bundle.d.ts │ ├── staff-bundle-not-yet-loaded.ts │ ├── start-page.ts │ ├── start-stuff.ts │ ├── store-getters.ts │ ├── tags │ │ ├── tags.styl │ │ └── tags.ts │ ├── theme.styl │ ├── third-party.styl │ ├── topbar │ │ ├── topbar.styl │ │ └── topbar.ts │ ├── translations.d.ts │ ├── tsconfig.json │ ├── util │ │ ├── ExplainingDropdown.styl │ │ ├── ExplainingDropdown.ts │ │ ├── FadingBackdrop.styl │ │ ├── FadingBackdrop.ts │ │ ├── resizable.styl │ │ ├── scrollbox.styl │ │ └── stupid-lightbox.js │ ├── utils │ │ ├── DropdownModal.styl │ │ ├── DropdownModal.ts │ │ ├── calcScrollRectIntoViewCoords.js │ │ ├── close-cross.styl │ │ ├── css-popup.styl │ │ ├── detect-mouse.ts │ │ ├── fade-in-on-click.styl │ │ ├── fade-in-on-click.ts │ │ ├── highlight-active-link-in-header.ts │ │ ├── page-scroll-mixin.ts │ │ ├── post-json.styl │ │ ├── react-utils.ts │ │ ├── scroll-into-view.ts │ │ ├── show-and-highlight.ts │ │ ├── site-utils.styl │ │ ├── talkyard-tour.styl │ │ ├── talkyard-tour.ts │ │ ├── util-browser.js │ │ ├── util.js │ │ ├── utils.ts │ │ └── window-zoom-resize-mixin.ts │ ├── variables.styl │ ├── watchbar │ │ ├── watchbar.styl │ │ └── watchbar.ts │ ├── widgets.styl │ ├── widgets.ts │ └── widgets │ │ └── widget-open-buttons.ts ├── app-staff │ ├── admin │ │ ├── AdminGuide.staff.ts │ │ ├── admin-app.staff.ts │ │ ├── admin-page.styl │ │ ├── api-panel.staff.ts │ │ ├── api-panel.styl │ │ ├── backup-panel.staff.ts │ │ ├── contents-panel.staff.ts │ │ ├── hostname-editor.staff.ts │ │ ├── hostname-editor.styl │ │ ├── inspect-panel.styl │ │ ├── inspect.staff.ts │ │ ├── oop-method.staff.ts │ │ ├── review-all.staff.ts │ │ ├── review.styl │ │ ├── special-contents.staff.ts │ │ ├── staff-tours.staff.ts │ │ ├── users-one.staff.ts │ │ └── users.staff.ts │ ├── create-site │ │ ├── create-site.staff.ts │ │ └── create-site.styl │ ├── misc-staff.styl │ ├── staff-bundle-already-loaded.d.ts │ ├── staff-prelude.staff.ts │ ├── superadmin │ │ ├── superadmin-app.staff.ts │ │ └── superadmin-app.styl │ └── tsconfig.json ├── embedded-comments │ ├── blog-comments.ts │ ├── comments-count.ts │ ├── debiki-utterscroll-iframe-parent.js │ ├── embedding-page.d.ts │ ├── fetch.ts │ ├── parent-footer.js │ ├── parent-header.js │ ├── readme.txt │ └── tsconfig.json ├── ext-iframe.js ├── macros │ ├── macros-dev.h │ ├── macros-none.h │ ├── macros-prod.h │ ├── macros.d.ts │ └── macros.ts ├── reactjs-types.ts ├── rtl │ ├── bootstrap.styl │ ├── react-select.styl │ ├── react-textarea-autocomplete.styl │ ├── right-to-left-mixins.styl │ └── right-to-left-props.styl ├── server │ ├── ReactActions.ts │ ├── ReactStore.ts │ ├── Server.ts │ ├── ServerApi.ts │ ├── avatar │ │ ├── AvatarAndName.ts │ │ └── avatar.ts │ ├── constants.ts │ ├── edit-history │ │ └── edit-history-dialog.ts │ ├── editor-bundle-not-yet-loaded.ts │ ├── editor │ │ ├── CdnLinkifyer.ts │ │ ├── link-previews-markdown-it-plugin.editor.ts │ │ ├── mentions-markdown-it-plugin.ts │ │ └── title-editor.ts │ ├── forum │ │ ├── edit-intro-dialog.ts │ │ └── forum.ts │ ├── help │ │ ├── help.ts │ │ └── serverAnnouncements.ts │ ├── links.ts │ ├── login │ │ ├── login-dialog.ts │ │ ├── login-if-needed.ts │ │ └── login.ts │ ├── magic-time.ts │ ├── me-getters.ts │ ├── model.ts │ ├── more-bundle-not-yet-loaded.ts │ ├── notification │ │ ├── Notification.ts │ │ └── notf-prefs-button.ts │ ├── oop-methods.ts │ ├── page-dialogs │ │ ├── delete-post-dialog.ts │ │ ├── flag-dialog.ts │ │ ├── move-posts-dialog.ts │ │ ├── open-share-popup.ts │ │ ├── see-wrench-dialog.ts │ │ ├── share-dialog.ts │ │ ├── tags-dialog.ts │ │ └── wikify-dialog.ts │ ├── page-methods.ts │ ├── page-tools │ │ └── page-tools.ts │ ├── page │ │ ├── arrows.ts │ │ ├── cats-or-home-link.ts │ │ ├── chat.ts │ │ ├── discussion.ts │ │ ├── hacks.ts │ │ ├── metabar.ts │ │ ├── page.ts │ │ ├── post-actions.ts │ │ ├── scroll-buttons.ts │ │ └── social-buttons.ts │ ├── personas │ │ └── PersonaIndicator.ts │ ├── prelude.ts │ ├── react-bootstrap-old │ │ └── Input.more.ts │ ├── react-elements │ │ └── name-login-btns.ts │ ├── readme.txt │ ├── rules.ts │ ├── server-side-type-stubs.ts │ ├── server-vars.ts │ ├── staff-bundle-not-yet-loaded.ts │ ├── store-getters.ts │ ├── tags │ │ └── tags.ts │ ├── topbar │ │ └── topbar.ts │ ├── translations.d.ts │ ├── tsconfig.json │ ├── util │ │ └── ExplainingDropdown.ts │ ├── utils │ │ ├── DropdownModal.ts │ │ ├── page-scroll-mixin.ts │ │ ├── react-utils.ts │ │ ├── scroll-into-view.ts │ │ ├── show-and-highlight.ts │ │ ├── utils.ts │ │ └── window-zoom-resize-mixin.ts │ ├── widgets.ts │ └── widgets │ │ └── widget-open-buttons.ts ├── serviceworker │ ├── constants.ts │ ├── magic-time.ts │ ├── model.ts │ ├── service-worker.ts │ └── tsconfig.json ├── third-party │ ├── abbreviate-jquery.js │ ├── bliss.shy.js │ ├── codemirror-show-markdown-line-breaks.css │ ├── codemirror-show-markdown-line-breaks.js │ ├── diff_match_patch.js │ ├── get-set-cookie.js │ ├── gifffer │ │ ├── LICENSE │ │ ├── README.md │ │ └── gifffer.js │ ├── html-css-sanitizer-bundle.js │ ├── jquery-scrollable.js │ ├── jquery.browser.js │ ├── lodash-custom.js │ ├── non-angular-slugify.js │ ├── popuplib.js │ ├── popuplib.js.debiki-readme.txt │ ├── rename-key-to-keymaster.js │ ├── smoothscroll-tiny.js │ ├── stupid-lightbox.css │ ├── third-party-types.d.ts │ └── tiny-querystring.umd.js └── types-and-const-enums.ts ├── conf ├── app-dev.conf ├── app-prod.conf ├── logback-prod.xml ├── logback.xml ├── rdb │ └── postgresql.conf └── routes ├── d ├── c ├── kill-down-prod-test ├── n ├── node ├── selenium └── tykc ├── decisions.adoc ├── docker-compose-no-limits.yml ├── docker-compose.it.yml ├── docker-compose.yml ├── docs ├── CLA-history.md ├── CLA-v1.txt ├── CLA-v2.txt ├── abbreviations.txt ├── about-the-talkyard-images.md ├── anti-bugs.txt ├── api │ └── openapi-TySeV0Search-test.json ├── building-images.md ├── coding-style.adoc ├── db-queries.txt ├── db-schema │ ├── README.txt │ ├── alt_page_ids3.txt │ ├── api_secrets3.txt │ ├── audit_log3.txt │ ├── backup_test_log3.txt │ ├── blocks3.txt │ ├── categories3.txt │ ├── domains-no-comments.txt │ ├── domains.txt │ ├── drafts3.txt │ ├── emails_out3.txt │ ├── flyway_schema_history.txt │ ├── group_participants3.txt │ ├── guest_prefs3.txt │ ├── hosts3.txt │ ├── identities3.txt │ ├── idps_t.txt │ ├── invites3.txt │ ├── job_queue_t.txt │ ├── link_previews_t.txt │ ├── links_t.txt │ ├── notices_t.txt │ ├── notifications3.txt │ ├── page_html_cache_t.txt │ ├── page_notf_prefs_t.txt │ ├── page_paths3.txt │ ├── page_popularity_scores3.txt │ ├── page_users3.txt │ ├── pages3.txt │ ├── perms_on_pages3.txt │ ├── post_actions3.txt │ ├── post_read_stats3.txt │ ├── post_revisions3.txt │ ├── post_tags3.txt │ ├── posts3.txt │ ├── review_tasks3.txt │ ├── sessions_t.txt │ ├── settings3.txt │ ├── sites3.txt │ ├── spam_check_queue3.txt │ ├── system_settings_t.txt │ ├── tag_notf_levels3.txt │ ├── tags_t.txt │ ├── tagtypes_t.txt │ ├── upload_refs3.txt │ ├── uploads3.txt │ ├── user_emails3.txt │ ├── user_stats3.txt │ ├── user_visit_stats3.txt │ ├── usernames3.txt │ ├── users3.txt │ ├── webhook_reqs_out_t.txt │ └── webhooks_t.txt ├── debugging-ios-safari-without-iphone.md ├── design-docs │ ├── bookmarks.dd.adoc │ └── tags.dd.adoc ├── developing-talkyard.md ├── docs-to-write.txt ├── e2e-tests-readme.md ├── everything-is-a-node.txt ├── intro-to-docker-compose.md ├── maybe-do-later.txt ├── maybe-refactor.txt ├── naming-notes.md ├── old │ └── css-docs.txt ├── safari-itp-firefox-etp.md ├── security-tests-readme.md ├── starting-talkyard.md ├── talkyard-api.md ├── testing-images-in-vagrant.md ├── tests-map.txt ├── things-in-talkyard.md ├── tips.md ├── ty-security.adoc ├── tyworld.adoc ├── ux-design.adoc └── wildcard-dot-localhost.md ├── gulpfile.js ├── images ├── app │ ├── Dockerfile.dev │ ├── Dockerfile.prod │ ├── assets │ │ └── .gitkeep │ ├── entrypoint.dev.sh │ └── fakemail-publ-test-self-signed.crt ├── backup │ └── entrypoint.sh ├── cache │ └── Dockerfile ├── certgen │ └── Dockerfile ├── fakemail │ ├── README.md │ ├── fakemail-publ-test-self-signed.crt │ ├── fakemail-publ-test-self-signed.key │ └── mailslurper-config.json ├── fakeweb │ ├── Dockerfile │ ├── app │ │ ├── deps.ts │ │ └── main.ts │ └── docker │ │ ├── LICENSE │ │ └── docker-entrypoint.sh ├── gulp │ ├── Dockerfile │ └── entrypoint.sh ├── keycloak │ ├── Dockerfile │ └── docker-compose-keycloak.yml ├── rdb │ ├── Dockerfile │ ├── chown-logs-then-exec-entrypoint.sh │ └── docker-entrypoint-initdb.d │ │ └── init.sh ├── search │ ├── Dockerfile │ ├── elasticsearch.yml │ ├── entrypoint.sh │ ├── jvm.options │ └── log4j2.properties └── web │ ├── README.adoc │ ├── assets │ └── .gitkeep │ ├── fonts │ └── .gitkeep │ ├── gen-def-cert-letsencrypt-key.sh │ ├── html │ ├── 403-upload-not-found.html │ ├── 404.html │ ├── 413.html │ ├── 502.html │ ├── 503.html │ ├── 504.html │ ├── robots.txt │ ├── security.txt │ └── session-iframe.html │ ├── http-limits.conf │ ├── http-redirect-to-https.conf │ ├── nginx.conf │ ├── old │ ├── Dockerfile-openresty-1.19.3.1-0-alpine-official │ ├── Dockerfile-openresty-1.19.9.1-alpine-3.14-official │ ├── Dockerfile.buster-apt-get-old │ ├── Dockerfile.nginx-old │ └── old-nginx-config-snippets.conf │ ├── openresty.Dockerfile │ ├── package.json │ ├── run-envsubst-gen-keys.sh │ ├── server-limits.conf │ ├── server-listen.conf │ ├── server-location-cdn.conf │ ├── server-locations.conf │ ├── server-ssl.conf │ ├── sites-enabled-manual │ └── default-server.conf │ ├── ssl-cert-snakeoil.key │ ├── ssl-cert-snakeoil.pem │ ├── ty-lua │ ├── access-by-lua-file.lua │ ├── init-by-lua-file.lua │ ├── init-worker-by-lua-file.lua │ ├── log-by-lua-file.lua │ ├── lua-limit-bandwidth │ │ ├── access-phase.lua │ │ ├── log-phase.lua │ │ ├── readme.md │ │ └── util.lua │ └── lua-limit-uploads │ │ └── limit-uploads.lua │ └── yarn.lock ├── lock.sbt ├── modules ├── ed-prod-one-test-override.yml └── paseto-cmd │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.txt │ └── src │ └── main.rs ├── nix ├── sources.json └── sources.nix ├── old ├── PinController.scala ├── db-skip.sql ├── done.txt └── perhaps.txt ├── package.json ├── project ├── Dependencies.scala ├── build.properties └── plugins.sbt ├── s ├── _tyd-completion-bash.sh ├── build-prod-images.sh ├── bump-versions.sh ├── d ├── d-cli ├── d-debug-security-tests ├── d-down ├── d-gulp ├── d-killcli ├── d-killdown ├── d-logs ├── d-logsf0 ├── d-psql ├── d-restart ├── d-restart-web-app ├── d-run-security-tests ├── d-start-ed-prod-one-test ├── d-stats ├── d-up-d-logsf0 ├── delete-container-logs.sh ├── diff-ty-dirs.sh ├── drop-database-create-empty.sh ├── drop-database-import-latest.sh ├── gen-big-file-if-not-exists.sh ├── git-delete-branch-here-and-origin.sh ├── git-delete-tag-here-and-origin.sh ├── git-diff-patches.sh ├── git-merge-into-release-branch.sh ├── git-range-stats.sh ├── git-show-changed-files.sh ├── git-show-tags-by-date.sh ├── impl │ ├── build-prod-app-image.sh │ ├── build-prod-images.sh │ ├── tyd-e2e-tests.ts │ ├── tyd-util.ts │ └── unjson.sh ├── kill-wdio ├── old │ ├── run-all-tests.sh │ ├── run-embedded-comments-tests.sh │ ├── run-test-suite.sh │ └── timeout.sh ├── promote-version.sh ├── rerun-failed-e2e.sh ├── run-e2e-tests.sh ├── selenium-config.js ├── selenium-install ├── selenium-start ├── selenium-start-invisible ├── start-prod-images.sh ├── tsconfig.json ├── tyd ├── tyd.js ├── tyd.ts ├── wdio ├── wdio-7 ├── wdio-debug-9101 └── yarn ├── shell.nix ├── tests ├── api │ ├── jest-concise-console.ts │ ├── jest.config.ts │ ├── server.ts │ └── tests │ │ └── some.test.ts ├── app │ ├── controllers │ │ ├── EmbeddedTopicsControllerSpec.scala │ │ ├── LoginWithSecretControllerSpec.scala │ │ └── UtilsSpec.scala │ ├── debiki │ │ ├── RateLimiterSpec.scala │ │ ├── SettingsSpec.scala │ │ ├── SiteCreatorSpec.scala │ │ ├── TextAndHtmlTest.scala │ │ └── dao │ │ │ ├── AnonymAppSpec.scala │ │ │ ├── CategoriesDaoAppSpec.scala │ │ │ ├── CreateSiteDaoAppSpec.scala │ │ │ ├── DaoAppSuite.scala │ │ │ ├── DeletePageAppSpec.scala │ │ │ ├── DraftsDaoAppSpec.scala │ │ │ ├── FirstPostsAppSpec.scala │ │ │ ├── MaxLimitsDaoAppSpec.scala │ │ │ ├── MessagesDaoAppSpec.scala │ │ │ ├── MovePostsAppSpec.scala │ │ │ ├── PageNotfPrefTxSpec.scala │ │ │ ├── PromotionToFullMemberAppSpec.scala │ │ │ ├── ReviewStuffAppSuite.scala │ │ │ ├── ReviewTasksAppSpec.scala │ │ │ ├── TagsAppSpec.scala │ │ │ ├── TestSiteAndDao.scala │ │ │ ├── ThreatLevelsAppSpec.scala │ │ │ ├── UploadsDaoSpec.scala │ │ │ ├── UserStatsAppSpec.scala │ │ │ └── __DaoAppSpecTemplate__.scala │ ├── resources │ │ └── embedding-pages │ │ │ ├── .gitignore │ │ │ ├── embeds-localhost-topic-id-1001-jquery-1.7-and-modernizr-2.5-pre-loaded.html │ │ │ ├── embeds-localhost-topic-id-1001-jquery-2.1-and-modernizr-2.7-pre-loaded.html │ │ │ ├── embeds-localhost-topic-id-1001-jquery-2.1-pre-loaded.html │ │ │ ├── embeds-localhost-topic-id-1001-modernizr-2.7-pre-loaded.html │ │ │ ├── embeds-localhost-topic-id-1001.html │ │ │ ├── embeds-localhost-topic-id-1002.html │ │ │ ├── embeds-site-1-topic-id-empty.html │ │ │ ├── embeds-site-11-topic-id-empty.html │ │ │ ├── embeds-site-12-topic-id-empty.html │ │ │ ├── embeds-site-13-topic-id-empty.html │ │ │ ├── embeds-site-14-topic-id-empty.html │ │ │ ├── embeds-site-15-topic-id-empty.html │ │ │ ├── readme.txt │ │ │ ├── static-page-at-10.0.2.2.html │ │ │ ├── static-page-no-id-2.html │ │ │ ├── static-page-no-id-3.html │ │ │ ├── static-page-no-id-4.html │ │ │ ├── static-page-no-id-5.html │ │ │ ├── static-page-no-id-6.html │ │ │ ├── static-page-no-id-7.html │ │ │ ├── static-page-no-id-8.html │ │ │ ├── static-page-no-id-9.html │ │ │ ├── static-page-no-id.html │ │ │ ├── static-page-with-jquery-and-modernizr.html │ │ │ └── static-page.html │ ├── server │ │ └── talkyard │ │ │ └── server │ │ │ ├── events │ │ │ └── WebhooksSiteDaoMixinSpec.scala │ │ │ └── search │ │ │ └── SearchQueryParserSpec.scala │ ├── talkyard │ │ └── server │ │ │ ├── GlobalConfigSpec.scala │ │ │ ├── authn │ │ │ ├── AuthOidcAppSpec.scala │ │ │ ├── LoginAppSpec.scala │ │ │ └── SignupAppSpec.scala │ │ │ ├── dao │ │ │ ├── LoadWhomToSendSummariesToAppSpec.scala │ │ │ ├── SiteTransactionAppSpec.scala │ │ │ ├── SiteTxLoadParticipantsAppSpec.scala │ │ │ ├── SiteTxNoticesAppSpec.scala │ │ │ └── SiteTxPermissionsAppSpec.scala │ │ │ ├── events │ │ │ └── WebhooksAppSpec.scala │ │ │ ├── linkpreviews │ │ │ ├── LinkPreviewMatchersSpec.scala │ │ │ └── LinkPreviewRendererSpec.scala │ │ │ ├── links │ │ │ └── LinksAppSpec.scala │ │ │ ├── notf │ │ │ ├── NotfsAppMentionsSpec.scala │ │ │ └── NotfsAppPageNotfsSpec.scala │ │ │ ├── security │ │ │ └── ReservedNamesTest.scala │ │ │ ├── sitepatch │ │ │ ├── DumpMaker.scala │ │ │ ├── SitePatcherAppSpec.scala │ │ │ └── TwoPeopleChatSpecTrait.scala │ │ │ └── summaryemails │ │ │ └── SummaryEmailsAppSpec.scala │ └── test │ │ └── tags │ │ └── SlowTest.java ├── e2e-wdio7 │ ├── modules │ ├── package.json │ ├── pageobjects │ │ ├── login.page.ts │ │ ├── page.ts │ │ └── secure.page.ts │ ├── pub-api.ts │ ├── specs │ │ ├── __e2e-test-template.2br.ec.e2e__.ts │ │ ├── __e2e-test-template.2br.f.e2e__.ts │ │ ├── alias-anons-approve-review.2br.f.e2e.ts │ │ ├── alias-anons-basic-perm.2br.f.e2e.ts │ │ ├── alias-anons-basic-temp.2br.f.e2e.ts │ │ ├── alias-anons-edit-alter.2br.f.e2e.ts │ │ ├── alias-anons-true-mixed.2br.f.e2e.ts │ │ ├── api-get-query-for-pats.2br.e2e.ts │ │ ├── api-list-query-for-topics-popular-first.1br.f.e2e.ts │ │ ├── api-search-ext-site-and-server.2br.cors.e2e.ts │ │ ├── api-search-full-text.1br.f.e2e.ts │ │ ├── api-upsert-posts.2br.d.e2e.ts │ │ ├── assign-can-see.2br.d.e2e.ts │ │ ├── assign-to-basic.2br.d.e2e.ts │ │ ├── assign-to-notfs.2br.d.e2e.ts │ │ ├── backlinks-basic.2br.d.e2e.ts │ │ ├── badges-basic.2br.e2e.ts │ │ ├── block-dir-msgs.2br.d.e2e.ts │ │ ├── block-mentions.2br.d.e2e.ts │ │ ├── cannot-reply-via-email.2br.e2e.ts │ │ ├── categories-basic.3br.d.e2e.ts │ │ ├── category-perms.2br.d.e2e.ts │ │ ├── cats-perf-many.2br.d.e2e.ts │ │ ├── chat-basic.2br.f.mtime.e2e.ts │ │ ├── chat-scroll.2br.f.e2e.ts │ │ ├── comment-sort-order-inherited.d.2br.e2e.ts │ │ ├── comment-sort-order.d.2br.e2e.ts │ │ ├── comment-sort-order.util.ts │ │ ├── create-private-site-gmail-invite-only.2br.f.e2e.ts │ │ ├── create-private-site-password.2br.f.e2e.ts │ │ ├── create-site-admin-guide.2br.d.e2e.ts │ │ ├── create-site-facebook.1br.d.extidp.e2e.ts │ │ ├── create-site-github-uppercase-email.1br.d.extidp.e2e.ts │ │ ├── create-site-gmail-and-email-notf.1br.d.extidp.e2e.ts │ │ ├── create-site-linkedin.1br.d.extidp.e2e.ts │ │ ├── create-site-password-run-admin-intro-tours.1br.d.e2e.ts │ │ ├── custom-forms.3br.d.e2e.ts │ │ ├── d.oidc-azure-impl.ts │ │ ├── d.oidc-azure-login-required.2br.extidp.e2e.ts │ │ ├── d.oidc-azure-pub-site.2br.extidp.e2e.ts │ │ ├── d.sessions-logout-elsewhere.4br.e2e.ts │ │ ├── d.sessions-staff-logout-others.4br.e2e.ts │ │ ├── dir.create-site-imp-json.2br.e2e.ts │ │ ├── dir.create-site-via-api.2br.e2e.ts │ │ ├── dir.manual.2br.e2e.ts │ │ ├── dir.summarize-squash-siblings.2br.e2e.ts │ │ ├── direct-messages-notfs.3br.d.e2e.ts │ │ ├── do-api-create-pages-comts-check-webhooks-search.2br.e2e.ts │ │ ├── do-api-like-and-subscribe.2br.e2e.ts │ │ ├── do-api-upvote-ideas-sort-by-votes.2br.d.e2e.ts │ │ ├── editor-toolbar-preview.1br.e2e.ts │ │ ├── embcom.comment-counts.2br.ec.cors.e2e.ts │ │ ├── embcom.create-site-req-verif-email-exit-tours.2br.e2e.ts │ │ ├── embcom.create-site-via-api.2br.e2e.ts │ │ ├── embcom.dont-load-script-twice.1br.ec.e2e.ts │ │ ├── embcom.drafts-previews-not-logged-in.2br.e2e.ts │ │ ├── embcom.expimpjson.create-site-exp-json.2br.e2e.ts │ │ ├── embcom.expimpjson.imp-to-existing-site.2br.e2e-UNIMPL.ts │ │ ├── embcom.expimpjson.imp-to-new-site.2br.e2e.ts │ │ ├── embcom.expimpjson.import-tests-impl.ts │ │ ├── embcom.expimpjson.restore-overwrite-site-new-domain.2br.e2e.ts │ │ ├── embcom.expimpjson.restore-overwrite-site-same-domain.2br.e2e.ts │ │ ├── embcom.expimpjson.test-data.ts │ │ ├── embcom.ignore-query-params.2br.e2e.ts │ │ ├── embcom.log-levels-on-loaded.1br.ec.e2e.ts │ │ ├── embcom.manual.2br.e2e.ts │ │ ├── embcom.manyframes.basic.2br.e2e.ts │ │ ├── embcom.manyframes.comment-counts.2br.cors.e2e.ts │ │ ├── embcom.manyframes.drafts-repl-to.2br.e2e.ts │ │ ├── embcom.manyframes.js-api.2br.ec.e2e.ts │ │ ├── embcom.manyframes.js-api.impl.ts │ │ ├── embcom.manyframes.js-api.sso.2br.ec.e2e.ts │ │ ├── embcom.manyframes.manual.2br.e2e.ts │ │ ├── embcom.reply-vote-report-bef-login.2br.e2e.ts │ │ ├── embcom.scroll-and-load-more.2br.ec.e2e.ts │ │ ├── embcom.sessions-emb-sess-cannot-moderate.3br.e2e.ts │ │ ├── embcom.sort-order-op-likes-btn-txt.2br.ec.e2e.ts │ │ ├── embcom.sso.redir-page.2br.ec.e2e.ts │ │ ├── embcom.sso.token-direct-w-logout-url.2br.ec.e2e.ts │ │ ├── embcom.sso.token-in-cookie.2br.ec.e2e.ts │ │ ├── embcom.sso.token-in-cookie.2br.test.ts--e2e-crypto-probl.txt │ │ ├── embcom.vote-bef-page-exists.1br.e2e.ts │ │ ├── embforum.b3c.login.2br.ef.e2e.ts │ │ ├── embforum.b3c.sso-login.2br.ef.e2e.ts │ │ ├── forum-sort-and-scroll.d.2br.e2e.ts │ │ ├── frag-action-compose-topic.2br.f.wip.ts │ │ ├── ghost.embcom.comments-basic.2br.e2e.ts │ │ ├── hide-unhide-tips.2br.f.e2e.ts │ │ ├── link-previews-all-others.1br.d.extln.e2e.ts │ │ ├── link-previews-http-to-https.1br.d.e2e.ts │ │ ├── link-previews-images-mp4-youtube.1br.d.extln.e2e.ts │ │ ├── link-previews-internal-may-see.2br.d.e2e.ts │ │ ├── link-previews-internal-not-see-cat.2br.d.e2e.ts │ │ ├── link-previews-internal-to-cats-not-see.2br.f.e2e.ts │ │ ├── link-previews-twitter-max-editor.1br.d.extln.e2e.ts │ │ ├── load-test-100-pages-60-users.2br.e2e.ts │ │ ├── load-test-site-builder.ts │ │ ├── may-see-email-adrs.2br.d.e2e.ts │ │ ├── modn-ban-from-disc-page.2br.f.e2e.ts │ │ ├── modn-ban-spammer.2br.f.e2e.ts │ │ ├── modn-from-disc-page-appr-befr.2br.f.e2e.ts │ │ ├── move-posts-newer-page-reply.2br.d.e2e.ts │ │ ├── move-posts-other-page.2br.d.e2e.ts │ │ ├── move-posts-pin-delete.2br.d.e2e.ts │ │ ├── move-posts-same-page.2br.d.e2e.ts │ │ ├── notfs-mark-seen-as-seen.d.2br.e2e.ts │ │ ├── notfs-prefs-inherit-group.d.2br.e2e.ts │ │ ├── notfs-prefs-inherit-own.d.2br.e2e.ts │ │ ├── page-type-discussion-progress.1br.d.e2e.ts │ │ ├── page-type-idea-statuses-comments.2br.d.e2e.ts │ │ ├── page-type-info-page.1br.d.e2e.ts │ │ ├── page-type-problem-statuses.2br.d.e2e.ts │ │ ├── page-type-question-closed.2br.d.e2e.ts │ │ ├── password-login-reset.2br.f.e2e.ts │ │ ├── perms-see-own.2br.f.e2e.ts │ │ ├── plan-maintenance.2br.d.e2e.ts │ │ ├── privacy-list-activity.2br.f.e2e.ts │ │ ├── privacy-may-see.3br.f.e2e.ts │ │ ├── private-chat.3br.d.e2e.ts │ │ ├── reindex-sites.2br.f.e2e.ts │ │ ├── search-tag-vals-priv-cats.2br.f.e2e.ts │ │ ├── settings-toggle-login-required.3br.d.e2e.ts │ │ ├── show-admin-notices.2br.e2e.ts │ │ ├── site-api-secrets-not-global.2br.f.e2e.ts │ │ ├── tags-badges-not-missing.2br.e2e.ts │ │ ├── tags-basic.2br.e2e.ts │ │ ├── topic-prominent-pats-basic.2br.d.e2e.ts │ │ ├── topic-prominent-pats-reply-approve.2br.d.e2e.ts │ │ ├── user-self-delete-upd-groups.2br.f.e2e.ts │ │ ├── votes-and-best-first.d.2br.e2e.ts │ │ ├── webhooks-basic.2br.e2e.ts │ │ ├── webhooks-enable-disable.2br.e2e.ts │ │ ├── webhooks-for-api-upserts.2br.e2e.ts │ │ ├── webhooks-retry-impl.ts │ │ └── webhooks-retry.2br.e2e.ts │ ├── target │ ├── test-constants.ts │ ├── test-types.ts │ ├── test-types2.ts │ ├── tsconfig.json │ ├── utils │ │ ├── do-api-actions.ts │ │ ├── emails-e2e.ts │ │ ├── ext-cors-site.html │ │ ├── fakeweb.ts │ │ ├── log-and-die.ts │ │ ├── make.ts │ │ ├── server.ts │ │ ├── settings-exp-def.ts │ │ ├── settings.ts │ │ ├── site-builder.ts │ │ ├── ty-assert.ts │ │ ├── ty-e2e-post.ts │ │ ├── ty-e2e-test-browser.ts │ │ └── utils.ts │ ├── wdio-progress-reporter.ts │ ├── wdio.conf.autogen-webdriver-7.ts │ ├── wdio.conf.ts │ └── yarn.lock ├── e2e │ ├── find-and-run-wdio.sh │ ├── package.json │ ├── pub-api.ts │ ├── readme.md │ ├── specs │ │ ├── __e2e-test-template__.2br.test.ts │ │ ├── admin-move-hostname.2browsers.test.ts │ │ ├── admin-review-cascade-approval.2br.mtime.test.ts │ │ ├── admin-review-invalidate-for-reply.2br.mtime.test.ts │ │ ├── admin-review-invalidate-page-deld.2br.mtime.test.ts │ │ ├── admin-user-approve-reject.2browsers.test.ts │ │ ├── admin-user-staff.2browsers.test.ts │ │ ├── admin-user-suspend.2browsers.test.ts │ │ ├── admin-user-threat-mild.2br.mtime.test.ts │ │ ├── admin-user-threat-moderate.2br.mtime.test.ts │ │ ├── all-links.test.ts │ │ ├── api-list-query-for-posts.test.ts │ │ ├── api-list-query-for-topics-recent-etc-first.test.ts │ │ ├── api-private-chat-two-pps-impl.test.ts │ │ ├── api-private-chat-two-pps-list-use-usernames.2browsers.test.ts │ │ ├── api-private-chat-two-pps-sso-extid.2browsers.test.ts │ │ ├── api-update-user-and-sso-user.2br.test.ts │ │ ├── api-upsert-categories.2browsers.test.ts │ │ ├── api-upsert-page-notfs.2browsers.test.ts │ │ ├── api-upsert-pages.2browsers.test.ts │ │ ├── api-w-sso-upsert-pages.2browsers.test.ts │ │ ├── authz-basic-see-reply-create.test.ts │ │ ├── authz-view-as-stranger.test.ts │ │ ├── categories-delete.2br.test.ts │ │ ├── chat-create-from-direct-message.2browsers.test.ts │ │ ├── chat-create-from-profile-pages.2browsers.test.ts │ │ ├── delete-pages.2br.test.ts │ │ ├── direct-messages-delete.2browsers.test.ts │ │ ├── drafts-chat-adv-ed.2browsers.test.ts │ │ ├── drafts-delete.test.ts │ │ ├── drafts-new-topic-from-cats-page.test.ts │ │ ├── drafts-new-topic.2br.mtime.test.ts │ │ ├── drafts-reply-edit-dir-msg.2br.mtime.test.ts │ │ ├── embcom.all-idp-logins-old-name.1br.extidp.test.ts │ │ ├── embcom.all-idp-logins.1br.extidp.test.ts │ │ ├── embcom.b3c.guest.1br.test.ts │ │ ├── embcom.b3c.unverif-gmail.1br.extidp.test.ts │ │ ├── embcom.b3c.verif-email.1br.test.ts │ │ ├── embcom.b3c.verif-gmail.1br.extidp.test.ts │ │ ├── embcom.manua.2br.test.ts │ │ ├── embedded-comments-cat-refs-and-disc-ids.2browsers.test.ts │ │ ├── embedded-comments-category-refs.2browsers.test.ts │ │ ├── embedded-comments-conf-notf-pref-first.test.ts │ │ ├── embedded-comments-create-site-forum-intro-tour.test.ts │ │ ├── embedded-comments-create-site-import-disqus.2br.test.ts │ │ ├── embedded-comments-create-site-no-verif-email-admin-area-tour.2browsers.test.ts │ │ ├── embedded-comments-different-disc-ids-same-page.test.ts │ │ ├── embedded-comments-discussion-id-old-name.test.ts │ │ ├── embedded-comments-discussion-id.test.ts │ │ ├── embedded-comments-edit-and-vote-old-name.test.ts │ │ ├── embedded-comments-edit-and-vote.test.ts │ │ ├── embedded-comments-gatsby.test.ts │ │ ├── embedded-comments-guest-login-email-notf-unsbscribe.test.ts │ │ ├── embedded-comments-navigation-as-guest.test.ts │ │ ├── embedded-comments-scroll-embedding-page.test.ts │ │ ├── embedded-comments-short-script-cache-time.test.ts │ │ ├── embedded-comments-uploads-origin.test.ts │ │ ├── flag-guest-block-agree.2browsers.test.ts │ │ ├── flag-member-block-agree.2browsers.test.ts │ │ ├── forum-drafts-not-logged-in.2browsers.test.ts │ │ ├── gmail-fb-join-login.extidp.1br.test.ts │ │ ├── group-mentions-built-in-groups.2browsers.test.ts │ │ ├── group-mentions-custom-groups.2browsers.test.ts │ │ ├── group-permissions-similar-topics.2br.mtime.test.ts │ │ ├── group-profile-change-things.2browsers.test.ts │ │ ├── imp-exp-imp-exp-site.test.ts │ │ ├── impersonate-post-as-other.2browsers.test.ts │ │ ├── impersonate-restricted-areas.test.ts │ │ ├── invite-to-groups.2browsers.test.ts │ │ ├── invites-by-adm-click-email-set-pwd-link.2browsers.test.ts │ │ ├── invites-by-core-try-login-after.2browsers.test.ts │ │ ├── invites-by-mod-try-signup-after.2browsers.test.ts │ │ ├── invites-many-retry.2browsers.test.ts │ │ ├── invites-too-many.2browsers.test.ts │ │ ├── invites-weird-email-addrs.2browsers.test.ts │ │ ├── login-expire-idle-after.2br.mtime.test.ts │ │ ├── login-required-ext-signup-login.1br.extidp.test.ts │ │ ├── login-required-join-global-chat.2br.test.ts │ │ ├── manual.2browsers.test.ts │ │ ├── many-users-mention-list-join-group.2browsers.test.ts │ │ ├── mod-review.2br.mtime.test.ts │ │ ├── modn-appr-bef-comb-w-revw-aftr.2br.mtime.test.ts │ │ ├── modn-approve-before.2br.mtime.test.ts │ │ ├── modn-from-disc-page-review-after.2browsers.test.ts │ │ ├── modn-review-after.2br.mtime.test.ts │ │ ├── navigation-as-admin.test.ts │ │ ├── navigation-as-impl.ts │ │ ├── navigation-as-member.test.ts │ │ ├── navigation-as-stranger.test.ts │ │ ├── new-member-allow-approve.2br.mtime.test.ts │ │ ├── new-user-review-ok.2br.mtime.test.ts │ │ ├── notf-emails-discussion.2br.mtime.test.ts │ │ ├── notf-override-group-prefs.2browsers.test.ts │ │ ├── notf-page-cats-site-UNIMPL.2browsers.test.ts │ │ ├── notf-prefs-custom-groups.2browsers.test.ts │ │ ├── notf-prefs-pages-replied-to.2br.test.ts │ │ ├── notf-prefs-private-groups.2browsers.test.ts │ │ ├── notfs-like-votes.2browsers.test.ts │ │ ├── notfs-mark-all-as-read.2browsers.test.ts │ │ ├── notfs-page-gone.2browsers.test.ts │ │ ├── notfs-snooze-talk.2br.mtime.test.ts │ │ ├── permissions-edit-wiki-posts.2browsers.test.ts │ │ ├── promote-demote-by-staff-join-leave-chats.2br.test.ts │ │ ├── review-edits-ninja-late.2br.mtime.test.ts │ │ ├── sanitize-posts.2browsers.test.ts │ │ ├── search-private-chat.2browsers.test.ts │ │ ├── search-public-basic.2browsers.test.ts │ │ ├── settings-allow-local-signup-UNIMPL.test.ts │ │ ├── settings-allow-signup-UNIMPL.test.ts │ │ ├── settings-allowed-email-domains.extidp.2br.test.ts │ │ ├── settings-approve-members.2browsers.test.ts │ │ ├── slow-3g-navigate-edit-drafts.2browsers.test.ts │ │ ├── spam-basic-akismet-blocked.2br.mtime.test.ts │ │ ├── spam-basic-akismet-false-negatives.2br.mtime.test.ts │ │ ├── spam-basic-akismet-false-positives.2br.mtime.test.ts │ │ ├── spam-basic-local-ip-links-unblock.2br.mtime.test.ts │ │ ├── spam-basic-local.2browsers.test.ts │ │ ├── spam-basic-safe-browsing-api-blocked.2br.mtime.test.ts │ │ ├── sso-access-denied-login.2browsers.test.ts │ │ ├── sso-admin-extra-login.test.ts │ │ ├── sso-all-ways-to-login.2browsers.test.ts │ │ ├── sso-approval-required.UNIMPL.2browsers.test.ts │ │ ├── sso-login-and-approval-required.UNIMPL.2browsers.test.ts │ │ ├── sso-login-member-impl.2browsers.test.ts │ │ ├── sso-login-member.2browsers.test.ts │ │ ├── sso-login-new-members.2browsers.test.ts │ │ ├── sso-login-required-w-logout-url.2browsers.test.ts │ │ ├── sso-login-required.2browsers.test.ts │ │ ├── sso-one-time-key-errors.2browsers.test.ts │ │ ├── sso-test.2browsers.test.ts │ │ ├── sso.logout-url.2br.test.ts │ │ ├── summary-emails.2br.mtime.test.ts │ │ ├── unsubscribe.2browsers.test.ts │ │ ├── upload-images-and-files.2br.test.ts │ │ ├── user-profile-access.test.ts │ │ ├── user-profile-cannot-delete-idp-email.1br.extidp.test.ts │ │ ├── user-profile-change-email.2browsers.test.ts │ │ ├── user-profile-change-password.2br.mtime.test.ts │ │ ├── user-profile-change-username.test.ts │ │ ├── utx-all-logins.1br.extidp.test.ts │ │ ├── utx-impl.ts │ │ ├── utx-is-fair-UNIMPL.test.ts │ │ ├── view-edit-history.2br.mtime.test.ts │ │ └── weird-usernames.2browsers.test.ts │ ├── target │ ├── test-constants.ts │ ├── test-types.ts │ ├── test-types2.ts │ ├── tsconfig.json │ ├── utils │ │ ├── log-and-die.ts │ │ ├── make.ts │ │ ├── pages-for.ts │ │ ├── server.ts │ │ ├── settings-exp-def.ts │ │ ├── settings.ts │ │ ├── site-builder.ts │ │ ├── ty-assert.ts │ │ └── utils.ts │ ├── wdio-progress-reporter.ts │ ├── wdio.conf.js │ ├── wdio.conf.ts │ └── yarn.lock ├── emb-forum-irame-1.html ├── int-w │ ├── ghost │ │ └── casper-post.hbs │ └── matrix │ │ ├── element-web-config.json │ │ └── homeserver.yaml └── test-media ├── to-talkyard ├── .gitignore ├── README.md ├── jest.config.js ├── n ├── package.json ├── src │ ├── from-disqus-to-ty.ts │ ├── from-wordpress-to-ty.ts │ ├── to-talkyard.d.ts │ └── to-talkyard.ts ├── tsconfig.json ├── tslint.json └── yarn.lock ├── translations ├── de_DE ├── en_US │ └── i18n.ts ├── es_CL ├── he_IL ├── i18n-README.md ├── lv_LV ├── nl_NL ├── pl_PL ├── pt_BR │ └── i18n.ts ├── ru_RU │ └── i18n.ts ├── sv_SE │ └── i18n.ts ├── uk_UA └── zh_CN ├── vendors └── nixos-signing-pub-key.gpg ├── version.txt ├── volumes └── gulp-home │ └── .gitkeep ├── wip ├── aliases │ ├── auto-test-thoughts.txt │ └── wip.txt ├── bookmarks │ └── bookmarks-wip.txt ├── cr-priv-prefs.txt ├── joint-decisions │ └── joint-decisions.txt ├── priv-comts │ └── priv-comts-wip.txt ├── priv-prefs │ └── priv-prefs-wip.txt ├── tags │ └── tags-wip.txt └── upgr-scala-2.13 │ └── add-collection-seq-import.sh └── yarn.lock /.npmignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *~ 11 | *.sass-cache 12 | 13 | # OS or Editor folders 14 | .DS_Store 15 | .cache 16 | .project 17 | .settings 18 | .tmproj 19 | nbproject 20 | Thumbs.db 21 | 22 | # NPM packages folder. 23 | node_modules/ 24 | 25 | # Brunch folder for temporary files. 26 | tmp/ 27 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [ 3 | 97 4 | ], 5 | "editor.tabSize": 2, 6 | "files.watcherExclude": { 7 | "**/node_modules": true, 8 | "**/dist": true, 9 | "**/build": true, 10 | "**/volumes": true, 11 | "**/target": true, 12 | "**/.bloop": true, 13 | "**/.metals": true, 14 | "**/.ammonite": true 15 | } 16 | } -------------------------------------------------------------------------------- /.yarnclean: -------------------------------------------------------------------------------- 1 | # Selenium drivers 2 | # What? This has somehow disappeared, 2020-12-13. Running via Docker now anyway. 3 | # ------ 4 | #!node_modules/selenium-standalone/.selenium/ 5 | # Will keep e.g.: 6 | # node_modules/selenium-standalone/.selenium/chromedriver/2.25-x64-chromedriver 7 | # ------ 8 | 9 | # test directories 10 | __tests__ 11 | test 12 | tests 13 | powered-test 14 | 15 | # asset directories 16 | # Let's keep docs — nice to have, if offline? [yarnclean_keep_docs] 17 | #docs 18 | #doc 19 | website 20 | images 21 | assets 22 | 23 | # examples 24 | example 25 | examples 26 | 27 | # code coverage directories 28 | coverage 29 | .nyc_output 30 | 31 | # build scripts 32 | Makefile 33 | Gulpfile.js 34 | Gruntfile.js 35 | 36 | # configs 37 | .tern-project 38 | .gitattributes 39 | .editorconfig 40 | .*ignore 41 | !./node_modules/.gitignore 42 | .eslintrc 43 | .jshintrc 44 | .flowconfig 45 | .documentup.json 46 | .yarn-metadata.json 47 | .*.yml 48 | *.yml 49 | 50 | # misc 51 | *.gz 52 | 53 | # Let's keep docs — nice to have, if offline? [yarnclean_keep_docs] 54 | #*.md 55 | -------------------------------------------------------------------------------- /appsv/model/.gitignore: -------------------------------------------------------------------------------- 1 | scratch.txt 2 | scratch/ 3 | 4 | # Maven 5 | target/ 6 | 7 | # SBT 8 | lib_managed/ 9 | src_managed/ 10 | project/ 11 | 12 | # Idea 13 | debiki-core.iml 14 | debiki-core.ipr 15 | debiki-core.iws 16 | 17 | # Eclipse 18 | .cache 19 | .classpath 20 | .project 21 | .settings/ 22 | -------------------------------------------------------------------------------- /appsv/model/build.sbt: -------------------------------------------------------------------------------- 1 | name := "ty-model" 2 | 3 | organization := "com.debiki" 4 | 5 | version := ProjectDirectory.versionFileContents 6 | 7 | // Apparently needed for SBT dependencyTree, see plugins.sbt. [dependencyTree_dependency] 8 | resolvers += "Scala-Tools Maven2 Repository" at "https://scala-tools.org/repo-releases" 9 | 10 | libraryDependencies ++= Seq( 11 | Dependencies.Libs.scalactic, 12 | Dependencies.Libs.guava, 13 | Dependencies.Libs.apacheCommonsCodec, 14 | Dependencies.Libs.apacheCommonsValidator, 15 | Dependencies.Libs.apacheCommonsEmail, // needed here for email address validation only 16 | Dependencies.Libs.apacheTika, 17 | Dependencies.Libs.owaspEncoder, 18 | Dependencies.Play.json, 19 | // COULD move to the server module? [mv_scrypt_2_srv] 20 | Dependencies.Libs.lambdaworksScrypt, 21 | // CLEAN_UP remove Mockito and Spec2. Use only ScalaTest, need to edit some tests. 22 | Dependencies.Libs.mockito, 23 | Dependencies.Libs.specs2, 24 | Dependencies.Libs.scalaTest, 25 | ) 26 | 27 | compileOrder := CompileOrder.JavaThenScala 28 | -------------------------------------------------------------------------------- /appsv/model/src/main/java/name/fraser/neil/plaintext/diff_match_patch.java: -------------------------------------------------------------------------------- 1 | ../../../../../../../../../modules/google-diff-match-patch/java/src/name/fraser/neil/plaintext/diff_match_patch.java -------------------------------------------------------------------------------- /appsv/model/src/main/scala/com/debiki/core/Notice.scala: -------------------------------------------------------------------------------- 1 | package com.debiki.core 2 | 3 | import play.api.libs.json.JsObject 4 | 5 | case class Notice( 6 | siteId: SiteId, 7 | toPatId: PatId, 8 | noticeId: NoticeId, 9 | firstAt: WhenMins, 10 | lastAt: WhenMins, 11 | numTotal: i32, 12 | noticeData: Opt[JsObject], 13 | ) { 14 | // For now, admins only. 15 | require(toPatId == Group.AdminsId, "TyE40fMJ2W4") 16 | require(noticeId >= 1001, "TyE5R02MRSEG4") 17 | require(firstAt.millis <= lastAt.millis, "TyE70SRDE55F") 18 | require(numTotal >= 1, "TyE70SRDE550") 19 | require(noticeData.isEmpty, "TyE40fMJ25MG") 20 | } 21 | 22 | 23 | object Notice { 24 | val TwitterLoginConfigured = 1001 25 | val TwitterLoginUsed = 1002 26 | } 27 | -------------------------------------------------------------------------------- /appsv/rdb/.gitignore: -------------------------------------------------------------------------------- 1 | scratch.txt 2 | scratch/ 3 | 4 | # SBT 5 | target/ 6 | lib_managed/ 7 | src_managed/ 8 | project/boot/ 9 | project/target/ 10 | 11 | # Idea 12 | .idea/ 13 | debiki-dao-oracle.iml 14 | project/project.iml 15 | 16 | # Eclipse 17 | .cache 18 | .classpath 19 | .project 20 | .settings/ 21 | -------------------------------------------------------------------------------- /appsv/rdb/build.sbt: -------------------------------------------------------------------------------- 1 | name := "ty-dao-rdb" 2 | 3 | organization := "com.debiki" 4 | 5 | version := ProjectDirectory.versionFileContents 6 | 7 | libraryDependencies ++= Seq( 8 | Dependencies.Play.json, 9 | Dependencies.Libs.postgresqlJbcdClient, 10 | Dependencies.Libs.flywaydb) 11 | 12 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2014/v3__no_email.sql: -------------------------------------------------------------------------------- 1 | -- This evolution starts using Null instead of "" if email absent (it is for Twitter users). 2 | 3 | alter table DW1_IDS_OPENID alter column EMAIL drop not null; 4 | update DW1_IDS_OPENID set EMAIL = null where length(EMAIL) = 0; 5 | alter table DW1_IDS_OPENID add constraint DW1_IDS_EMAIL__C_LEN check (length(EMAIL) between 1 and 100); 6 | 7 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2014/v5__multireply.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table DW1_PAGE_ACTIONS add column MULTIREPLY varchar; 3 | alter table DW1_PAGE_ACTIONS add constraint DW1_PGAS_TYPE_MULTIREPLY__C check (TYPE = 'Post' or MULTIREPLY is null); 4 | alter table DW1_PAGE_ACTIONS add constraint DW1_PGAS_MULTIREPLY__C_NUM check (MULTIREPLY ~ '[0-9,]'); 5 | 6 | alter table DW1_POSTS add column MULTIREPLY varchar; 7 | alter table DW1_POSTS add constraint DW1_POSTS_MULTIREPLY__C_NUM check (MULTIREPLY ~ '[0-9,]'); 8 | 9 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2014/v6__always_commonmark.sql: -------------------------------------------------------------------------------- 1 | alter table DW1_PAGE_ACTIONS drop column MARKUP; 2 | alter table DW1_POSTS drop column MARKUP; 3 | 4 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2014/v8__email_for_each_new_post.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table DW1_USERS add column EMAIL_FOR_EVERY_NEW_POST boolean not null default false; 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2014/v9__page_role_homepage.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Add 'H'omepage role. Rename 'Generic' to Web'P'age (not 'W'iki'P'age). 3 | alter table DW1_PAGES drop constraint DW1_PAGES_PAGEROLE__C_IN; 4 | update DW1_PAGES set PAGE_ROLE = 'P' where PAGE_ROLE = 'G'; 5 | alter table DW1_PAGES add constraint DW1_PAGES_PAGEROLE__C_IN check (PAGE_ROLE in ( 6 | 'H', 'P', 'EC', 'B', 'BP', 'F', 'FC', 'FT', 'W', 'WP', 'C', 'SP')); 7 | 8 | -- Convert own pages. 9 | update DW1_PAGES set PAGE_ROLE = 'H' where TENANT = '3' and GUID in ( 10 | '4xsq1', -- the homepage 11 | '4vkm1'); -- the embedded comments page 12 | 13 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2015/v10__create_main_site.sql: -------------------------------------------------------------------------------- 1 | 2 | insert into dw1_tenants (id, name) 3 | select '1', 'Main Site' 4 | where not exists ( 5 | select id from dw1_tenants where id = '1'); 6 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2015/v11__site_creator_email.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table dw1_tenants drop column creator_tenant_id; 3 | alter table dw1_tenants drop column creator_role_id; 4 | 5 | alter table dw1_tenants add column creator_email_address varchar; 6 | alter table dw1_tenants add constraint dw1_tnt_creatoremail__c 7 | check (creator_email_address like '%@%.%'); 8 | 9 | update dw1_tenants set creator_email_address = 'unknown@example.com'; 10 | update dw1_tenants set creator_ip = '0.0.0.0' where creator_ip is null; 11 | update dw1_tenants set name = 'embedded-site-' || id where name is null; 12 | 13 | alter table dw1_tenants alter creator_email_address set not null; 14 | alter table dw1_tenants alter creator_ip set not null; 15 | alter table dw1_tenants alter name set not null; 16 | 17 | create index dw1_tenants_creatoremail on dw1_tenants(creator_email_address); 18 | 19 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2015/v20__target_site_id.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table dw2_audit_log add column target_site_id varchar; 3 | alter table dw2_audit_log add constraint dw2_auditlog_tgtsite__r__sites 4 | foreign key (target_site_id) references dw1_tenants(id); 5 | 6 | -- Previously, a column 'sno' (sequence no) later renamed to 'id' was the primary key. 7 | alter table dw1_identities drop constraint dw1_idsoid_sno__p; 8 | alter table dw1_identities add constraint dw1_ids_siteid_id__p primary key (site_id, id); 9 | 10 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2015/v23__problems_ideas.sql: -------------------------------------------------------------------------------- 1 | 2 | -- When a problem or idea was accepted / planned to be fixed/done. 3 | alter table dw1_pages add column planned_at timestamp; 4 | 5 | update dw1_pages set planned_at = created_at where done_at is not null; 6 | update dw1_pages set closed_at = done_at where done_at is not null and closed_at is null; 7 | update dw1_pages set closed_at = answered_at where answered_at is not null and closed_at is null; 8 | 9 | alter table dw1_pages add constraint dw1_pages_createdat_plannedat__c_le check (created_at <= planned_at); 10 | alter table dw1_pages add constraint dw1_pages_plannedat_doneat__c_le check (planned_at <= done_at); 11 | alter table dw1_pages add constraint dw1_pages_plannedat_doneat__c_null check ( 12 | done_at is null or planned_at is not null); 13 | 14 | -- A topic gets closed when it's marked as done or answered. 15 | alter table dw1_pages add constraint dw1_pages__c_closed_if_done_answered check ( 16 | (done_at is null and answered_at is null) or closed_at is not null); 17 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2015/v26__longer_site_hostname.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Allow long hostnames, so e2e tests can use descriptive hostnames. 3 | -- Change from varchar(50) to varchar any-length, then add a constraint instead. 4 | alter table dw1_tenant_hosts alter host type varchar; 5 | alter table dw1_tenant_hosts add constraint dw1_hosts_host__c_len 6 | check(length(host) between 1 and 100); 7 | 8 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2015/v27__longer_emails.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Allow longer email columns, e.g. body too short (only 2000). 3 | -- First remove varchar(NNN) length, then add constraints instead. 4 | alter table dw1_emails_out alter sent_to type varchar; 5 | alter table dw1_emails_out alter subject type varchar; 6 | alter table dw1_emails_out alter body_html type varchar; 7 | alter table dw1_emails_out alter provider_email_id type varchar; 8 | alter table dw1_emails_out alter failure_text type varchar; 9 | 10 | alter table dw1_emails_out add constraint dw1_emlot_sentto__c_len check(length(sent_to) <= 200); 11 | alter table dw1_emails_out add constraint dw1_emlot_subject__c_len check(length(subject) between 1 and 200); 12 | alter table dw1_emails_out add constraint dw1_emlot_bodyhtml__c_len check(length(body_html) between 1 and 5000); 13 | alter table dw1_emails_out add constraint dw1_emlot_provideremailid__c_len check(length(provider_email_id) <= 200); 14 | alter table dw1_emails_out add constraint dw1_emlot_failuretext__c_len check(length(failure_text) <= 10000); 15 | 16 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2016/v35__html_tag_css_classes.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table dw1_pages add column html_tag_css_classes varchar; 3 | 4 | alter table dw1_pages add constraint dw1_pages_htmltagcssclass__c_len check ( 5 | length(html_tag_css_classes) between 1 and 100); 6 | 7 | 8 | -- Will be reused, if categories will get their own css class too. 9 | create or replace function is_valid_css_class(text varchar) returns boolean as $$ 10 | begin 11 | return text ~ '^[ a-zA-Z0-9_-]+$'; 12 | end; 13 | $$ language plpgsql; 14 | 15 | alter table dw1_pages add constraint dw1_pages_htmltagcssclass__c_ptrn check ( 16 | is_valid_css_class(html_tag_css_classes)); 17 | 18 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/flyway-migrations/y2016/v37__audit_log_email.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table dw2_audit_log add email_address varchar; 3 | alter table dw2_audit_log add constraint dw2_auditlog_emailaddr__c_len check ( 4 | length(email_address) between 3 and 200); 5 | alter table dw2_audit_log add constraint dw2_auditlog_emailaddr__c_email check ( 6 | email_address like '%_@_%'); 7 | 8 | 9 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/play-evolutions/15.sql: -------------------------------------------------------------------------------- 1 | # This evolution adds a per site next page id sequence no. 2 | 3 | 4 | # --- !Ups 5 | 6 | 7 | alter table DW1_TENANTS add column NEXT_PAGE_ID int not null default 1; 8 | 9 | create or replace function INC_NEXT_PAGE_ID(site_id character varying) returns int as $$ 10 | declare 11 | next_id int;; 12 | begin 13 | update DW1_TENANTS 14 | set NEXT_PAGE_ID = NEXT_PAGE_ID + 1 15 | where ID = site_id 16 | returning NEXT_PAGE_ID into next_id;; 17 | return next_id - 1;; 18 | end;; 19 | $$ language plpgsql; 20 | 21 | 22 | # --- !Downs 23 | 24 | 25 | alter table DW1_TENANTS drop column NEXT_PAGE_ID; 26 | 27 | drop function INC_NEXT_PAGE_ID(site_id character varying); 28 | 29 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/old/play-evolutions/8.sql: -------------------------------------------------------------------------------- 1 | -- This evolution is for embedded sites: 2 | -- - Adds EMBEDDING_PAGE_URL column to DW1_PAGES. 3 | 4 | 5 | # --- !Ups 6 | 7 | 8 | alter table DW1_PAGES add column EMBEDDING_PAGE_URL varchar; 9 | 10 | alter table DW1_PAGES add constraint DW1_PAGES_EMBPAGEURL__C_LEN check ( 11 | length(EMBEDDING_PAGE_URL) between 1 and 200); 12 | 13 | alter table DW1_PAGES add constraint DW1_PAGES_EMBPAGEURL__C_TRIM check ( 14 | trim(EMBEDDING_PAGE_URL) = EMBEDDING_PAGE_URL); 15 | 16 | 17 | # --- !Downs 18 | 19 | 20 | alter table DW1_PAGES drop column EMBEDDING_PAGE_URL; 21 | 22 | 23 | -------------------------------------------------------------------------------- /appsv/rdb/scripts/pending-evolutions.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Drop: dw1_pgas_magic_id_types__c 3 | or change 0t, 0b, 0c to 65501, 02, 03 4 | 5 | 6 | */ 7 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/wip_r__views.sql: -------------------------------------------------------------------------------- 1 | create or replace function pretty_email_status(status integer) returns varchar 2 | language plpgsql as $$ 3 | begin 4 | return case status 5 | when 1 then 'undecided' 6 | when 2 then 'skipped' 7 | when 3 then 'created' 8 | else '' || status 9 | end; 10 | end; 11 | $$; 12 | 13 | 14 | select *, pretty_email_status(email_status) as pr_email_status from notifications3 where site_id = -16; 15 | 16 | 17 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v10__remove_constraint.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table audit_log3 drop constraint dw2_auditlog_tgtsite__r__sites; 3 | 4 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v11__topic_list_style.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table pages3 add column layout bigint not null default 0; 3 | 4 | 5 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v13__page_hidden_at.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table pages3 add column hidden_at timestamp; 3 | alter table pages3 add constraint dw1_pages_createdat_hiddenat__c_le CHECK (created_at <= hidden_at); 4 | 5 | alter table audit_log3 drop constraint dw2_auditlog_page_post__c; 6 | delete from audit_log3 where did_what = 3; -- 3 = new page 7 | alter table audit_log3 add constraint dw2_auditlog_page_post__c check ( 8 | did_what <> 3 or (page_id is not null and post_id is not null)); 9 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v14__more_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column invite_only boolean; 3 | alter table settings3 add column allow_signup boolean; 4 | alter table settings3 add column allow_local_signup boolean; 5 | 6 | alter table settings3 drop constraint settings3_auth_guest__c; 7 | alter table settings3 add constraint settings3_auth_guest__c check ( 8 | not (allow_guest_login and ( 9 | user_must_be_auth or user_must_be_approved or invite_only or not allow_signup))); 10 | 11 | alter table settings3 add constraint settings3_signup__c check ( 12 | allow_signup or (not allow_local_signup)); 13 | 14 | 15 | alter table users3 add column about varchar; 16 | alter table users3 add constraint users_about__c_len check (length(about) between 1 and 2000); 17 | -- Guests have no about field. 18 | alter table users3 add constraint users_about_guest__c_n check (user_id >= 1 or about is null); 19 | 20 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v15__change_usernamed.sql: -------------------------------------------------------------------------------- 1 | 2 | create table usernames3( 3 | site_id varchar not null, 4 | username varchar not null, 5 | in_use_from timestamp not null, 6 | in_use_to timestamp, 7 | user_id int not null, 8 | first_mention_at timestamp, 9 | -- username + from...to should be unique, but we'll verify that in the app server instead. 10 | constraint usernames_p primary key (site_id, username, in_use_from), 11 | constraint usernames_r_users foreign key (site_id, user_id) references users3 (site_id, user_id), 12 | constraint usernames_c_from_lt_to check (in_use_from < in_use_to), 13 | constraint usernames_c_from_le_mention check (in_use_from <= first_mention_at), 14 | constraint usernames_c_mention_le_to check (first_mention_at <= in_use_to), 15 | constraint usernames_c_username_len check (length(username) between 2 and 50) 16 | ); 17 | 18 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v16__show_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column show_categories boolean; 3 | alter table settings3 add column show_topic_filter boolean; 4 | alter table settings3 add column show_topic_types boolean; 5 | alter table settings3 add column select_topic_type boolean; 6 | 7 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v3__unknown_user.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | -- The unknown user was never created because of a bug in the v1 script, se the_bug below 4 | insert into users3(site_id, user_id, display_name, email, guest_cookie, created_at) 5 | select '1', -3, 'Unknown', '-', 'UU', now_utc() 6 | where not exists ( 7 | select 1 from users3 where site_id = '1' and user_id = -3); -- the_bug: -3 was -1 8 | 9 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v4__index_queue.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | -- No foreign keys, because that'd prevent us from deleting the site, page or post. 4 | create table index_queue3( 5 | inserted_at timestamp not null default now_utc(), 6 | -- When the item was created or inserted into the queue or whatever. We sort by this 7 | -- field when determining in which order to index stuff. Or not? 8 | action_at timestamp not null, 9 | site_id varchar not null, 10 | site_version int not null, 11 | page_id varchar, 12 | page_version int, 13 | post_id int, 14 | post_rev_nr int, 15 | constraint ixq_site_page__u unique (site_id, page_id), 16 | constraint ixq_site_post__u unique (site_id, post_id), 17 | constraint ixq_page_or_post__c_xor check (page_id is null or post_id is null), 18 | constraint ixq_page_pageversion__c_nl_eq check ((page_id is null) = (page_version is null)), 19 | constraint ixq_post_postrevnr__c_nl_eq check ((post_id is null) = (post_rev_nr is null)) 20 | ); 21 | 22 | create index ixq_actionat__i on index_queue3 (action_at desc); 23 | 24 | 25 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v5__superadmin.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table sites3 add column status int not null default 1; 3 | alter table users3 add column is_superadmin boolean; 4 | 5 | alter table sites3 add constraint sites_status__c_in check (status between 1 and 7); 6 | 7 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v6__branch_sideways.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table posts3 add column branch_sideways smallint; 3 | 4 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2016/v9__spam_check_queue.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | -- No foreign keys, because that'd prevent us from deleting the post. 4 | create table spam_check_queue3( 5 | inserted_at timestamp not null default now_utc(), 6 | -- When the item was created or inserted into the queue or whatever. We sort by this 7 | -- field when determining in which order to check for spam. 8 | action_at timestamp not null, 9 | site_id varchar not null, 10 | post_id int not null, 11 | post_rev_nr int not null, 12 | user_id int not null, 13 | user_id_cookie varchar not null, 14 | browser_fingerprint int not null, 15 | req_uri varchar not null, 16 | req_ip varchar not null, 17 | req_user_agent varchar, 18 | req_referer varchar, 19 | 20 | constraint scq_site_post__p primary key (site_id, post_id) 21 | ); 22 | 23 | create index scq_actionat__i on spam_check_queue3 (action_at desc); 24 | 25 | 26 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v21__username_lowercase.sql: -------------------------------------------------------------------------------- 1 | 2 | update usernames3 set username = lower(username); 3 | alter table usernames3 rename username to username_lowercase; 4 | 5 | 6 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v22__backup_test_log.sql: -------------------------------------------------------------------------------- 1 | 2 | create table backup_test_log3( 3 | logged_at timestamp not null, 4 | logged_by varchar not null, 5 | backup_of_what varchar not null, 6 | random_value varchar not null, 7 | got_ok_message_at timestamp); 8 | 9 | create index backup_test_log_i on backup_test_log3 (logged_at desc); 10 | 11 | 12 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v24__page_pop.sql: -------------------------------------------------------------------------------- 1 | 2 | create table page_popularity_scores3( 3 | site_id int not null, 4 | page_id varchar not null, 5 | popular_since timestamp not null, 6 | updated_at timestamp not null, 7 | algorithm smallint not null, 8 | day_score float not null, 9 | week_score float not null, 10 | month_score float not null, 11 | quarter_score float not null, 12 | year_score float not null, 13 | all_score float not null, 14 | constraint pagepopscores_site_page_p primary key (site_id, page_id), 15 | constraint pagepopscores_r_pages foreign key (site_id, page_id) references pages3 (site_id, page_id), 16 | constraint pagepopscores_updat_ge_popsince check (updated_at >= popular_since), 17 | constraint pagepopscores_alg_gtz check (algorithm > 0) 18 | -- Scores can perhaps be < 0? If many Unwanted votes, e.g. no Likes, and orig post = unwanted? 19 | ); 20 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v25__summary_emails.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table users3 add column summary_email_interval_mins int; 3 | alter table users3 add column summary_email_if_active bool; 4 | 5 | alter table user_stats3 add column last_summary_email_at timestamp; 6 | alter table user_stats3 add column next_summary_email_at timestamp; 7 | 8 | alter table page_users3 add column incl_in_summary_email_at_mins int; 9 | 10 | create index userstats_nextsummary_i on user_stats3 (next_summary_email_at nulls first); 11 | 12 | alter table emails_out3 drop constraint dw1_emlot_type__c_in; 13 | alter table emails_out3 alter column "type" type smallint using ( 14 | case "type" 15 | when 'Notf' then 1 16 | when 'AcSm' then 2 17 | when 'Invt' then 11 18 | when 'InAc' then 12 19 | when 'InPw' then 13 20 | when 'CrAc' then 21 21 | when 'RsPw' then 22 22 | end); 23 | 24 | alter table emails_out3 add constraint emailsout_type__c_betw check ("type" between 1 and 50); 25 | 26 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v26__nulls_last.sql: -------------------------------------------------------------------------------- 1 | 2 | drop index userstats_nextsummary_i; 3 | create index userstats_nextsummary_i on user_stats3 (next_summary_email_at nulls last); 4 | 5 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v27__plan_done_any_role.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table pages3 drop constraint dw1_pages_role_answered__c; 3 | alter table pages3 drop constraint dw1_pages_role_planned_done__c; 4 | 5 | alter table user_stats3 rename column next_summary_email_at to next_summary_maybe_at; 6 | 7 | alter table pages3 add column incl_in_summaries smallint; 8 | alter table categories3 add column incl_in_summaries smallint; 9 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v28__allow_embedding.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column allow_embedding_from varchar; 3 | alter table settings3 add constraint settings_allowembeddingfrom__c_len check ( 4 | length(allow_embedding_from) between 5 and 100); 5 | 6 | create table alt_page_ids3( 7 | site_id int not null, 8 | alt_page_id varchar not null, 9 | real_page_id varchar not null, 10 | constraint altpageids_p primary key (site_id, alt_page_id), 11 | constraint altpageids_r_pages foreign key (site_id, real_page_id) references pages3(site_id, page_id), 12 | constraint altpageids_altid_c_len check (length(alt_page_id) between 1 and 300) 13 | ); 14 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v29__remove_columns.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Is now a settings3 column instead. 3 | alter table sites3 drop column embedding_site_url; 4 | 5 | -- These should be removed too, later: (use audit_log3 instead) 6 | alter table sites3 alter column creator_email_address drop not null; 7 | alter table sites3 alter column creator_ip drop not null; 8 | 9 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v30__page_started_at_wait_until.sql: -------------------------------------------------------------------------------- 1 | -- Delete the To-Do page role, use Idea in status Planned instead, that's the same thing, right. 2 | update pages3 3 | set page_role = 15 -- idea 4 | where page_role = 13; -- to do 5 | 6 | alter table pages3 add column started_at timestamp; 7 | alter table pages3 add column wait_until timestamp; 8 | 9 | alter table pages3 add constraint pages_c_createdat_le_startedat check (created_at <= started_at); 10 | alter table pages3 add constraint pages_c_not_todo check (page_role <> 13); -- todo 11 | 12 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v31__num_posts_total.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table pages3 add column num_posts_total int not null default 0; 3 | 4 | update pages3 pg set num_posts_total = ( 5 | select count(*) from posts3 po where pg.site_id = po.site_id and pg.page_id = po.page_id); 6 | 7 | alter table pages3 add constraint pages_c_numpoststotal_ge_numrepliestotal check ( 8 | num_posts_total >= num_replies_total); 9 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2017/v32__page_constraints.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table pages3 drop constraint dw1_pages_plannedat_doneat__c_null; 3 | alter table pages3 add constraint pages_c_plannedat_le_startedat check (planned_at <= started_at); 4 | alter table pages3 add constraint pages_c_startedat_le_doneat check (started_at <= done_at); 5 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2018/v34__longer_allow_embedding.sql: -------------------------------------------------------------------------------- 1 | 2 | -- This constraint might contain many hostnames, so let it be fairly long. 3 | alter table settings3 drop constraint settings_allowembeddingfrom__c_len; 4 | alter table settings3 add constraint settings_c_allowembeddingfrom_btw_5_300 check ( 5 | length(allow_embedding_from) between 5 and 300); 6 | 7 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2018/v368__no_browser_id_cookie.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table blocks3 alter browser_id_cookie drop not null; 3 | 4 | alter table spam_check_queue3 alter user_id_cookie drop not null; 5 | alter table spam_check_queue3 rename user_id_cookie to browser_id_cookie; 6 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2018/v36__publ_site_id.sql: -------------------------------------------------------------------------------- 1 | alter table sites3 add column publ_id varchar; 2 | 3 | -- This generates 10 random hex chars. Future publ ids will be 4 | -- generated by the app server instead, and use base32? 36? + a crypto random gen. 5 | update sites3 set publ_id = substring(md5(random()::text) from 0 for 10 + 1); 6 | 7 | alter table sites3 alter column publ_id set not null; 8 | 9 | create unique index sites_publid_u on sites3 (publ_id); 10 | 11 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2018/v370__bump_after_close.sql: -------------------------------------------------------------------------------- 1 | alter table pages3 drop constraint dw1_pages_bumpedat_le_closedat__c; 2 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2018/v374__many_invites.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Drop unique index, replace with non-unique. So one can re-send invites. 3 | drop index dw2_invites_email__u; 4 | create index invites_emailaddr_invby_i on invites3 (site_id, email_address, created_by_id); 5 | 6 | create index invites_invat_i on invites3 (site_id, created_at desc); 7 | 8 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2018/v376__guest_id_constr.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table users3 drop constraint dw1_users_guestcookie__c_len; 3 | alter table users3 rename guest_cookie to guest_browser_id; 4 | alter table users3 add constraint users_c_guestbrowserid_len check ( 5 | length(guest_browser_id) between 2 and 100); 6 | 7 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v377__tour_tips_seen.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table user_stats3 add column tour_tips_seen varchar[]; 3 | alter table user_stats3 add constraint userstats_c_tourtipsseen_len check ( 4 | pg_column_size(tour_tips_seen) <= 400); 5 | 6 | -- Don't show intro tours, for people who have old existing admin accounts already. 7 | -- They might have customized their communities, possibly breaking the tours. 8 | update user_stats3 us 9 | set tour_tips_seen ='{"a_c_a", "a_b_a", "f_c_a", "f_b_a"}' 10 | where us.user_id >= 100 11 | and exists ( 12 | select 1 from users3 u 13 | where u.site_id = us.site_id and u.user_id = us.user_id and u.is_admin); 14 | 15 | 16 | alter table users3 add column ui_prefs jsonb; 17 | alter table users3 add constraint users_c_uiprefs_len check ( 18 | pg_column_size(ui_prefs) <= 400); 19 | 20 | 21 | alter table settings3 add column expire_idle_after_mins int; 22 | 23 | 24 | -- Unpin about category topics. 25 | update pages3 set pin_order = null, pin_where = null where page_role = 9; 26 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v378__more_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column enable_gitlab_login boolean; 3 | alter table settings3 add column enable_linkedin_login boolean; 4 | alter table settings3 add column enable_vk_login boolean; 5 | alter table settings3 add column enable_instagram_login boolean; 6 | alter table settings3 add column enable_forum boolean; 7 | alter table settings3 add column enable_api boolean; 8 | alter table settings3 add column enable_tags boolean; 9 | 10 | alter table settings3 add column embedded_comments_category_id int; 11 | alter table settings3 add constraint settings_embcmtscatid_r_categories 12 | foreign key (site_id, embedded_comments_category_id) 13 | references categories3(site_id, id); 14 | 15 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v379__tos_privacy_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column terms_of_use_url varchar; 3 | alter table settings3 add column privacy_url varchar; 4 | alter table settings3 add column rules_url varchar; 5 | alter table settings3 add column contact_email_addr varchar; 6 | alter table settings3 add column contact_url varchar; 7 | 8 | 9 | alter table settings3 add constraint settings_c_termsofuseurl_len check ( 10 | length(terms_of_use_url) between 1 and 200); 11 | 12 | alter table settings3 add constraint settings_c_privacyurl_len check ( 13 | length(privacy_url) between 1 and 200); 14 | 15 | alter table settings3 add constraint settings_c_rulesurl_len check ( 16 | length(rules_url) between 1 and 200); 17 | 18 | alter table settings3 add constraint settings_c_contactemailaddr_len check ( 19 | length(contact_email_addr) between 1 and 200); 20 | 21 | alter table settings3 add constraint settings_c_contacturl_len check ( 22 | length(contact_url) between 1 and 200); 23 | 24 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v380__review_task_notfs.sql: -------------------------------------------------------------------------------- 1 | alter table notifications3 drop constraint dw1_notfs_type__c_in; 2 | update notifications3 set notf_type = notf_type + 300; 3 | update notifications3 set notf_type = 406 where notf_type = 306; 4 | alter table notifications3 add constraint notfs_c_notftype_range check (notf_type between 101 and 999); 5 | 6 | 7 | alter table settings3 drop constraint settings3_auth_guest__c; 8 | alter table settings3 add constraint settings_c_guestlogin_auth check (not ( 9 | allow_guest_login and ( 10 | user_must_be_auth or user_must_be_approved or invite_only))); 11 | 12 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v384__invites_add_to_group.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table invites3 add column start_at_url varchar; 3 | alter table invites3 add constraint invites_c_startaturl_len check ( 4 | length(start_at_url) between 1 and 100); 5 | 6 | alter table invites3 add column add_to_group_id int; 7 | alter table invites3 add constraint invites_addtogroup_r_pps -- ix: invites_i_addtogroupid 8 | foreign key (site_id, add_to_group_id) references users3 (site_id, user_id) deferrable; 9 | 10 | create index invites_i_addtogroupid on invites3 (site_id, add_to_group_id) 11 | where add_to_group_id is not null; 12 | 13 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v385__sso_logout_url.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column sso_login_required_logout_url varchar; 3 | alter table settings3 add constraint settings_c_ssologinrequiredlogouturl_len check ( 4 | length(sso_login_required_logout_url) between 1 and 200); 5 | 6 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v386__guests_wo_browserid_use_extid.sql: -------------------------------------------------------------------------------- 1 | 2 | drop index pps_u_site_guest_no_browser_id; 3 | alter table users3 add constraint pps_c_guest_w_no_browserid_has_extid check ( 4 | (user_id > 0) or (guest_browser_id is not null) or (ext_id is not null)); 5 | 6 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v387__email_length.sql: -------------------------------------------------------------------------------- 1 | alter table emails_out3 drop constraint dw1_emlot_bodyhtml__c_len; 2 | alter table emails_out3 add constraint emailsout_c_bodyhtml_len check ( 3 | length(body_html) between 1 and 20000); 4 | 5 | -- A Facebook avatar url was 263 chars long, so bump the previous 250 chars limit. 6 | alter table identities3 drop constraint dw1_ids_avatarurl__c_len; 7 | alter table identities3 add constraint identities_c_avatarurl_len check ( 8 | length(avatar_url) between 1 and 500); 9 | 10 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2019/v388__rm_email_notf_char_check.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table users3 drop constraint dw1_users_emlntf__c; 3 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v389__backup_test_log.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table backup_test_log3 add column file_name varchar; 3 | alter table backup_test_log3 add constraint backuptestlog_c_filename_len check ( 4 | length(file_name) between 1 and 200); 5 | 6 | alter table sites3 drop column price_plan; 7 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v391__root_cat_slug.sql: -------------------------------------------------------------------------------- 1 | update categories3 set slug = '__root_cat_' || id 2 | where parent_id is null; 3 | 4 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v392__cors_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column enable_cors boolean; 3 | alter table settings3 add column allow_cors_from varchar; 4 | alter table settings3 add column allow_cors_creds boolean; 5 | alter table settings3 add column cache_cors_prefl_secs int; 6 | 7 | alter table settings3 add constraint settings_c_allowcorsfrom_len check ( 8 | length(allow_cors_from) between 1 and 1000); 9 | 10 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v393__nav_conf_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column nav_conf jsonb; 3 | alter table settings3 add constraint settings_c_navconf_len check ( 4 | pg_column_size(nav_conf) <= 50000); 5 | 6 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v394__settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table settings3 add column start_of_body_html varchar; 3 | alter table settings3 add constraint settings_c_startofbodyhtml_len check ( 4 | length(start_of_body_html) between 1 and 50000); 5 | 6 | alter table user_stats3 add column snooze_notfs_until timestamp; 7 | alter table user_stats3 add column after_snooze_then int; 8 | 9 | 10 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v399__site_feature_flags.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table sites3 add column feature_flags_c varchar; 3 | alter table sites3 add constraint sites3_c_featureflags_len check ( 4 | length(feature_flags_c) between 1 and 500); 5 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2020/v400__missing_fx_ix.sql: -------------------------------------------------------------------------------- 1 | 2 | create index postsread_i_readbyid on post_read_stats3 (site_id, user_id); 3 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2021/v404__profile_pic_length.sql: -------------------------------------------------------------------------------- 1 | 2 | -- A Google profile pic URL was 830 chars, so let's allow up to, hmm, 3 | -- 2100, because there has to be *some* max limit, and IE11 apparently cuts urls 4 | -- at 2083 chars (URL path max 2048 chars). 5 | -- https://support.microsoft.com/en-us/topic/maximum-url-length-is-2-083-characters-in-internet-explorer-174e7c8a-6666-f4e0-6fd6-908b53c12246 6 | -- 7 | create domain pic_url_d as varchar; 8 | alter domain pic_url_d add constraint picurl_c_len_gz check (length(value) > 0); 9 | alter domain pic_url_d add constraint picurl_c_len_max check (length(value) <= 2100); 10 | 11 | alter table identities3 drop constraint idtys_c_pictureurl_len; 12 | alter table identities3 alter column picture_url_c type pic_url_d; 13 | 14 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2021/v407__upvote_features.adoc: -------------------------------------------------------------------------------- 1 | If any upgr problems, then: 2 | 3 | [source, sql] 4 | ---- 5 | -- Left trim urls: 6 | update settings3 set sso_logout_redir_url_c = regexp_replace( 7 | sso_logout_redir_url_c, '^(.*)(https?:\/\/.*)$', '\2') 8 | where sso_logout_redir_url_c is not null; 9 | update settings3 set sso_refresh_authn_token_url_c = regexp_replace( 10 | sso_refresh_authn_token_url_c, '^(.*)(https?:\/\/.*)$', '\2') 11 | where sso_refresh_authn_token_url_c is not null; 12 | 13 | -- Fix hostname: 14 | update settings3 set sso_logout_redir_url_c = regexp_replace( 15 | sso_logout_redir_url_c, 16 | '^(https?:\/\/[a-z0-9_.-]+(:[0-9]+)?)[^/]*(.*)$', '\1\3') 17 | where sso_logout_redir_url_c is not null; 18 | update settings3 set sso_refresh_authn_token_url_c = regexp_replace( 19 | sso_refresh_authn_token_url_c, 20 | '^(https?:\/\/[a-z0-9_.-]+(:[0-9]+)?)[^/]*(.*)$', '\1\3') 21 | where sso_refresh_authn_token_url_c is not null; 22 | ---- 23 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2021/v412__forum_search_box.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table pages3 add column forum_search_box_c i16_gz_d; 3 | alter table pages3 add column forum_main_view_c i16_gz_d; 4 | alter table pages3 add column forum_cats_topics_c i32_gz_d; 5 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2022/v414__webhooks_constr.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | create domain jsonb_ste100_000_d jsonb; 4 | alter domain jsonb_ste100_000_d add 5 | constraint jsonb_ste100_000_d_c_ste100_000 check (pg_column_size(value) <= 100000); 6 | 7 | create domain jsonb_ste250_000_d jsonb; 8 | alter domain jsonb_ste250_000_d add 9 | constraint jsonb_ste250_000_d_c_ste250_000 check (pg_column_size(value) <= 250000); 10 | 11 | create domain jsonb_ste500_000_d jsonb; 12 | alter domain jsonb_ste500_000_d add 13 | constraint jsonb_ste500_000_d_c_ste500_000 check (pg_column_size(value) <= 500000); 14 | 15 | alter table webhook_reqs_out_t alter column sent_json_c type jsonb_ste500_000_d; 16 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2022/v415__drop_reply_at_constr.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Was a too strict constraint. Sometimes an old comment gets moved to a new page, 3 | -- then its timestamps are older than the page. 4 | alter table pages3 drop constraint dw1_pages_createdat_replyat__c_le; 5 | 6 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2023/v421__sys_settings.sql: -------------------------------------------------------------------------------- 1 | 2 | alter table emails_out3 rename column id to email_id_c; 3 | alter table emails_out3 rename column type to out_type_c; 4 | 5 | alter table emails_out3 6 | alter column out_type_c type i16_gz_lt1000_d, 7 | add column out_sub_type_c i16_gz_lt1000_d, 8 | drop constraint emailsout_type__c_betw; 9 | 10 | 11 | create table system_settings_t ( 12 | maintenance_until_unix_secs_c i64_gz_d 13 | ); 14 | 15 | 16 | insert into system_settings_t (maintenance_until_unix_secs_c) values (null); 17 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2024/wip_v427__alias.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | alter table page_users3 add column prefer_alias_id_c pat_id_d; 4 | -- + 5 | -- fk deferred 6 | -- ix 7 | 8 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/resources/db/migration/y2999/wip_v41Y__samesite_none.sql: -------------------------------------------------------------------------------- 1 | 2 | create domain forgotten_d i16_d default 0; 3 | alter domain forgotten_d add 4 | constraint forgotten_d_c_in_0_1_2 check (value between 0 and 2); 5 | 6 | alter table sessions_t add column hash_4_ho_ss_none_c bytea_len32_d; 7 | alter table sessions_t rename column hash_4_http_only_c to hash_5_ho_ss_lax_c; 8 | alter table sessions_t rename column hash_5_strict_c to hash_6_ho_ss_strict_c; 9 | 10 | alter table sessions_t add column forgotten_c not null; 11 | 12 | 13 | create sessions_i_to_forget_a_bit on sessions_t (least(deleted_at_c, expired_at_c)) 14 | where forgotten_c = 0; 15 | 16 | create sessions_i_to_forget_more on sessions_t (least(deleted_at_c, expired_at_c)) 17 | where forgotten_c = 1; 18 | 19 | create sessions_i_ended_at on sessions_t (least(deleted_at_c, expired_at_c)); 20 | -------------------------------------------------------------------------------- /appsv/rdb/src/main/scala/db/migration/MigrationHelper.scala: -------------------------------------------------------------------------------- 1 | package db.migration 2 | 3 | import com.debiki.core.ScalaBasedDatabaseMigrations 4 | import com.debiki.dao.rdb.RdbSystemTransaction 5 | 6 | object MigrationHelper { 7 | 8 | var scalaBasedMigrations: ScalaBasedDatabaseMigrations = _ 9 | 10 | /** Makes a SystemDbDao available to Flyway Java migrations (well, Scala not Java). */ 11 | var systemDbDao: RdbSystemTransaction = _ 12 | 13 | } 14 | -------------------------------------------------------------------------------- /appsv/server/debiki/HtmlUtils.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Kaj Magnus Lindberg (born 1979) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as 6 | * published by the Free Software Foundation, either version 3 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package debiki 19 | 20 | 21 | 22 | object HtmlUtils { 23 | 24 | val OkCssClassRegexText = "[ a-zA-Z0-9_-]*" 25 | val OkCssClassRegex = OkCssClassRegexText.r 26 | 27 | } 28 | 29 | -------------------------------------------------------------------------------- /appsv/server/debiki/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Kaj Magnus Lindberg (born 1979) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as 6 | * published by the Free Software Foundation, either version 3 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | 19 | package object debiki { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /appsv/server/talkyard/server/dao/package.scala: -------------------------------------------------------------------------------- 1 | package talkyard.server 2 | 3 | import com.debiki.core.SiteTx 4 | 5 | package object dao { 6 | 7 | case class TxCtx(tx: SiteTx, staleStuff: StaleStuff) 8 | // + maxLimits, rateLimits? 9 | 10 | } 11 | -------------------------------------------------------------------------------- /appsv/server/talkyard/server/emails/in/package.scala: -------------------------------------------------------------------------------- 1 | package talkyard.server.emails 2 | 3 | import com.debiki.core._ 4 | 5 | 6 | package object in { 7 | 8 | 9 | case class ParsedReplyEmail( 10 | messageId: St, 11 | dateText: St, 12 | mailboxHash: Opt[St], 13 | sentToAddr: St, 14 | //sentToName: St, 15 | //sentToHash: St, 16 | sentFromAddr: St, 17 | //sentFromName: St, 18 | //sentFromHash: St, 19 | replyTo: St, 20 | subject: St, 21 | htmlBody: Opt[St], 22 | textBody: Opt[St], 23 | strippedReplyText: Opt[St], 24 | seemsLikeSpam: Opt[Bo], 25 | spamScore: Opt[f32], 26 | attachments: ImmSeq[ParsedAttachment], 27 | ) 28 | 29 | case class ParsedAttachment( 30 | name: St, // file name, e.g. "myimage.png" 31 | contentBase64: St, 32 | contentType: St, // e.g. "image/png" 33 | contentLength: i32, 34 | contentId: St, // e.g. "myimage.png@01CE7342.75E71F80" — what's that? 35 | ) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /appsv/server/talkyard/server/emails/out/package.scala: -------------------------------------------------------------------------------- 1 | package talkyard.server.emails 2 | 3 | import com.debiki.core._ 4 | 5 | 6 | package object out { 7 | 8 | // A bit more than a month. 9 | // Unsubscribe links had better work, also if someone was away on vacation 10 | // for a month, and, when back, wants to unsubscribe. So, 5 weeks? 11 | val MaxUnsubEmailAgeDays: i32 = 5 * 7 12 | 13 | } 14 | -------------------------------------------------------------------------------- /appsv/server/talkyard/server/emails/transl: -------------------------------------------------------------------------------- 1 | ../../../../../modules/ty-translations/emails -------------------------------------------------------------------------------- /appsv/server/talkyard/server/parser/ScalarsParSer.scala: -------------------------------------------------------------------------------- 1 | package talkyard.server.parser 2 | 3 | import com.debiki.core._ 4 | 5 | 6 | 7 | object ScalarsParSer { 8 | 9 | def parseSignOnId(idMaybe: St): SsoId = { 10 | if (idMaybe.isEmpty) throwBadInpData("TyE5603MRE245", "Sign-On ID is empty") 11 | idMaybe 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /appsv/server/talkyard/server/rendr/package.scala: -------------------------------------------------------------------------------- 1 | package talkyard.server 2 | 3 | import com.debiki.core._ 4 | 5 | 6 | package object rendr { 7 | 8 | case class RenderParams( 9 | embeddedOriginOrEmpty: St, 10 | allowClassIdDataAttrs: Bo, 11 | followLinks: Bo, 12 | ) 13 | 14 | case class NashornParams( 15 | siteIdHostnames: SiteIdHostnames, 16 | embeddedOriginOrEmpty: St, 17 | allowClassIdDataAttrs: Bo, 18 | followLinks: Bo, 19 | mayMention: Set[Username] => Map[Username, Bo], 20 | //mayMentionAll/Channel/...: Bo? 21 | ) { 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /appsv/server/views/adminlogin/oneTimeLoginLinkEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(siteAddress: String, url: String, member: com.debiki.core.User, expiresInMinutes: Int) 2 | 3 | @import org.owasp.encoder.Encode 4 | 5 | @* Sync with Bash script extracting the admin's name and the url [ADMLOGINEML] 6 | in the talkyard-prod-one repo. The script is useful if email not yet configured. *@ 7 |
8 |

Hi @{member.nameOrUsername},

9 | 10 |

11 | To login at @siteAddress, as @@@{member.username}, please follow 12 | 13 | this link. 14 | It expires in @expiresInMinutes minutes, and works just once. 15 |

16 | 17 |

Kind regards.

18 |
19 | 20 | -------------------------------------------------------------------------------- /appsv/server/views/createaccount/accountAlreadyExistsEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(emailAddress: String, siteAddress: String, globals: debiki.Globals) 2 | 3 | @origin = @{ globals.originOf(siteAddress) } 4 | 5 |
6 |

Hello,

7 | 8 |

9 | You attempted to create a new account at @siteAddress, 10 | with email address @emailAddress, but you already have such an account.@* [2WABJDD4_] *@ 11 | If you don't remember your password, you can 12 | 13 | reset it here. 14 |

15 | 16 |

Kind regards,
17 | Debiki 18 |

19 |
20 | -------------------------------------------------------------------------------- /appsv/server/views/createaccount/accountApprovedEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(newMember: com.debiki.core.UserInclDetails, siteHostname: String, siteOrigin: String) 2 | 3 |

4 | Welcome to @siteHostname, @{newMember.nameOrUsername} 5 |

6 | 7 |

Your account at @siteHostname has been approved. 8 | Now you can go there and talk with the others: 9 |

10 | 11 |

@siteOrigin

12 | 13 |

Kind regards.

14 | 15 | -------------------------------------------------------------------------------- /appsv/server/views/createaccount/createAccountLinkEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(siteAddress: String, username: String, safeVerificationUrl: String, expirationTimeInHours: Int, globals: debiki.Globals) 2 | 3 | @origin = @{ 4 | globals.originOf(siteAddress) 5 | } 6 | 7 | @* For links you don't want people to click, unless they know what they're doing. *@ 8 | @grayBoringLink(host: String) = { 9 | @host 10 | } 11 | 12 |
13 |

Hello,

14 | 15 |

@* About including @username, see: [20KCQ5Y] *@ 16 | To finish the creation of your account with username @username at @grayBoringLink(siteAddress), please click 17 | this link. 18 | It expires in @{expirationTimeInHours - 1} hours. 19 |

20 | 21 |

22 | Kind regards,
23 | Talkyard 24 |

25 |
26 | 27 | -------------------------------------------------------------------------------- /appsv/server/views/createaccount/newMemberToApproveEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(user: com.debiki.core.UserInclDetails, siteOrigin: String) 2 | 3 |

4 | New member waiting for approval: 5 | @@@{user.usernameParensFullName} 6 |

7 | 8 |

Go here to review and approve: 9 |

10 | 11 |

@siteOrigin/-/admin/users/waiting

12 | 13 |

Kind regards.

14 | 15 | -------------------------------------------------------------------------------- /appsv/server/views/createaccount/welcomePage.scala.html: -------------------------------------------------------------------------------- 1 | @(tpi: debiki.SiteTpi, returnToUrl: Option[String]) 2 | 3 | 4 | @css = @{(""" 5 | 6 | """)} 7 | 8 | @returnToUrlUnescapedHash = @{ 9 | returnToUrl.map(_.replaceAllLiterally("__dwHash__", "#")) 10 | } 11 | @views.html.createsite.main(tpi, css = css) { 12 |

Welcome!

13 | 14 |
15 |

You have verified your email address. You are now logged in.

16 | 17 |

18 | Continue... 20 |

21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /appsv/server/views/createsite/welcomeEmail.scala.html: -------------------------------------------------------------------------------- 1 | // Oops, not in use [welc_em_0used] 2 | @() 3 | @* 4 | @(site: com.debiki.core.Tenant, userName: String) 5 | 6 | @import controllers.CreateEmbeddedSiteController 7 | @import debiki.HtmlUtils.link 8 | 9 | @embeddingSiteUrl = @{ site.embeddingSiteUrl.get } 10 | @adminUrl = @{ CreateEmbeddedSiteController.adminUrlForEmbeddedSite(site.id) } 11 | 12 | @embeddedCommentsInstructionsUrl = @{ 13 | val embeddedSiteOrigin = debiki.Globals.siteByIdOrigin(site.id) 14 | embeddedSiteOrigin + routes.CreateEmbeddedSiteController.embeddingSiteInstructionsPage() 15 | } 16 | 17 |
18 |

Hi @userName,

19 | 20 |

Embedded comments have been setup for your website at @embeddingSiteUrl.

21 | 22 |

Please find here getting started instructions:

23 | 24 | Instructions 25 | 26 |

You can log in and administrate comments here:

27 | 28 |

@link(adminUrl)

29 | 30 |

Kind regards, Debiki
31 | www.debiki.com 32 |

33 |
34 | 35 | *@ 36 | -------------------------------------------------------------------------------- /appsv/server/views/debikiScriptsHead.scala.html: -------------------------------------------------------------------------------- 1 | @** 2 | * Copyright (c) 2013-2020 Kaj Magnus Lindberg 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as 6 | * published by the Free Software Foundation, either version 3 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | *@ 17 | 18 | @(tpi: debiki.SiteTpi) 19 | 20 | 21 | -------------------------------------------------------------------------------- /appsv/server/views/googleAnalytics.scala.html: -------------------------------------------------------------------------------- 1 | @(trackingId: String) 2 | 3 | 14 | 15 | -------------------------------------------------------------------------------- /appsv/server/views/invite/inviteAcceptedEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(siteHostname: String, invitedEmailAddress: String) 2 | 3 |

Hi,

4 | 5 |

You previously invited @{invitedEmailAddress} to join @siteHostname, 6 | and now he or she has accepted your invite (that is, he or she clicked 7 | the link in the invite email). 8 |

9 | 10 |

Kind regards.

11 | 12 | -------------------------------------------------------------------------------- /appsv/server/views/invite/inviteEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(inviterName: String, siteHostname: String, probablyUsername: String, secretKey: String, globals: debiki.Globals) 2 | 3 | @siteUrl = @{ globals.originOf(siteHostname) } 4 | 5 | @acceptInviteUrl = @{ 6 | val origin = globals.originOf(siteHostname) 7 | s"$origin${controllers.routes.InviteController.acceptInvite(secretKey)}" 8 | } 9 | 10 |

Hi,

11 | 12 |

@inviterName invites you to join@* [5FJBAW2_] *@ @siteHostname.

@* I18N *@ 13 | 14 |

Click the link below, if you are interested:

15 | 16 |

@acceptInviteUrl

17 | 18 |

An account will then be created for you and you will be logged in, automatically. 19 | Your username will be @probablyUsername (if available, and you can change it later). 20 | We will also send you a choose-password email. 21 |

22 | 23 |

Kind regards

24 | 25 | 26 | -------------------------------------------------------------------------------- /appsv/server/views/invite/welcomeSetPasswordEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(siteHostname: String, setPasswordUrl: String, username: String, globals: debiki.Globals) 2 | 3 |

Welcome to @siteHostname! And thanks for accepting the invitation.@* [5FJB2AZY_] I18N *@

4 | 5 |

We have created an account for you, with username @username. 6 | Please login with that username or your email address. 7 | And you need a password; click the link below to choose a password:

8 | 9 |

@setPasswordUrl

10 | 11 |

Kind regards

12 | 13 | -------------------------------------------------------------------------------- /appsv/server/views/login/accountsLinkedPleaseLoginAgain.scala.html: -------------------------------------------------------------------------------- 1 | @(tpi: debiki.SiteTpi, origNonceBack: String, tryLoginAgainUrl: String, idpName: String) 2 | 3 | @css = @{(""" 4 | """)} 5 | 6 | @* SECURITY SHOULD compare origNonceBack with the local storage nonce, and 7 | generate a new nonce and use in loginUrlNewNonce below. [br_authn_nonce] 8 | *@ 9 | @tryAgainUrlNewNonce = @{ 10 | tryLoginAgainUrl 11 | } 12 | 13 | @views.html.createsite.main(tpi, css = css) { 14 | 15 |

Done: Accounts linked.

16 |
17 | 18 |

19 | Log in again over at @idpName, please — this time, should work directly: 20 |

21 | 22 |

23 | Login again 24 |

25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /appsv/server/views/login/accountsNotLinkedPleaseLoginAgain.scala.html: -------------------------------------------------------------------------------- 1 | @(tpi: debiki.SiteTpi, origNonceBack: String, tryLoginAgainUrl: String, idpName: String) 2 | 3 | @css = @{(""" 4 | """)} 5 | 6 | @* SECURITY SHOULD compare origNonceBack with the local storage nonce, and 7 | generate a new nonce and use in loginUrlNewNonce below. [br_authn_nonce] 8 | *@ 9 | @tryAgainUrlNewNonce = @{ 10 | tryLoginAgainUrl 11 | } 12 | 13 | @views.html.createsite.main(tpi, css = css) { 14 | 15 |

Try again?

16 |
17 | 18 |

19 | Would you like to log in again, over at @idpName — 20 | with a different account this time? Or create a new? 21 |

22 | 23 |

24 | Login again 25 |

26 | 27 |

28 | @* UX SHOULD skip this for embedded comments pages? Then don't know what's at: / 29 | at the embedding website. *@ 30 | Go to topic list page / Home 31 |

32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /appsv/server/views/login/verifyYourEmailAddr.scala.html: -------------------------------------------------------------------------------- 1 | @(tpi: debiki.SiteTpi, subject: String, emailToVerify: String, expMins: Int) 2 | 3 | @css = @{(""" 4 | """)} 5 | 6 | @views.html.createsite.main(tpi, css = css) { 7 | 8 |

Verify your email address

9 |
10 | 11 |

We sent you an email, title: @subject 12 | 13 |

Check your email inbox: @emailToVerify

14 | 15 |

You can close this page.
16 | The link in the email expires in @expMins minutes. 17 |

18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /appsv/server/views/login/verifyYourEmailAddrEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(name: String, siteAddr: String, emailVerifUrl: String) 2 | 3 |
4 |

Hi @name,

5 | 6 |

7 | You (or someone else) are logging in at @siteAddr 8 | — please click this link, to verify your email address. 9 |

10 | 11 |

(If you don't know what this is about, please ignore this email.)

12 | 13 |

Kind regards. 14 |

15 |
16 | -------------------------------------------------------------------------------- /appsv/server/views/resetpassword/resetPasswordEmail.scala.html: -------------------------------------------------------------------------------- 1 | @(siteAddress: String, secret: String, userName: String, expiresInMinutes: Int, globals: debiki.Globals) 2 | 3 | @origin = @{ 4 | globals.originOf(siteAddress) 5 | } 6 | 7 |
8 |

Hi @userName,

9 | 10 |

11 | To reset your password at @siteAddress, please follow @* [RSTPWDLNK] *@ 12 | 13 | this link. It expires in @expiresInMinutes minutes. 14 |

15 | 16 |

Kind regards.
17 |

18 |
19 | -------------------------------------------------------------------------------- /appsv/server/views/scripts/googleAnalytics4.scala.html: -------------------------------------------------------------------------------- 1 | @(tagId: String) 2 | 3 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /appsv/server/views/summaryemails/unsubFromSummariesDonePage.scala.html: -------------------------------------------------------------------------------- 1 | @** 2 | * Copyright (c) 2017 Kaj Magnus Lindberg 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as 6 | * published by the Free Software Foundation, either version 3 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | *@ 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 |

Done.

28 | 29 | Go to the homepage 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /appsv/server/views/unsubscribe/youHaveBeenUnsubscribed.scala.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Done

4 | 5 |

You have been unsubscribed.

6 | 7 | @* This is confusing, for embedded comments sites — then should link back to the blog, 8 | instead of to the embedded site. So remove for now: 9 |

Go to homepage

10 | *@ 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/app-editor/editor-bundle-already-loaded.d.ts: -------------------------------------------------------------------------------- 1 | 2 | // Things in editor-bundle.js that we can access from outside the bundle, 3 | // once it's been loaded already. 4 | 5 | declare namespace debiki2.editor { 6 | 7 | function getOrCreateEditor(success: (editor: any) => void); 8 | 9 | function markdownToSafeHtml(source: string, hostAndPort?, sanitizerOptions?): string; 10 | 11 | } 12 | 13 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 14 | -------------------------------------------------------------------------------- /client/app-editor/editor-prelude.editor.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// -------------------------------------------------------------------------------- /client/app-editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | "types": ["react", "react-dom", "lodash", "core-js", "markdown-it"], 7 | // highlight.js, linkify-it, mdurl 8 | "outFile": "editor-typescript.js", 9 | "sourceMap": true, 10 | "inlineSources": true // include source code in mapping file 11 | }, 12 | "include": [ 13 | "**/*.ts", 14 | ], 15 | "exclude": [ 16 | "editor-bundle-already-loaded.d.ts", 17 | "**/*.spec.ts", 18 | "**/*.test.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /client/app-head/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | "types": ["core-js"], 7 | "outFile": "head-typescript.js", 8 | "sourceMap": true, 9 | "inlineSources": true // include source code in mapping file 10 | }, 11 | "include": [ 12 | "**/*.ts" 13 | ], 14 | "exclude": [ 15 | "**/*.spec.ts", 16 | "**/*.test.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /client/app-more/edit-history/edit-history-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .dw-edit-history 3 | width: auto; 4 | ins 5 | font-weight: bold; 6 | 7 | .dw-help 8 | margin-bottom: 1em; 9 | 10 | .ed-load-more-revs 11 | display: block; 12 | padding-left: 20px; 13 | padding-right: 20px; 14 | margin: 2.3em auto 0; 15 | 16 | max-width: calc(100vw - 80px); 17 | @media (min-width: 1480px) 18 | max-width: 1400px; 19 | 20 | .ed-revision 21 | &:not(:last-of-type) 22 | margin-bottom: 2em; 23 | 24 | .dw-p-by 25 | margin: 0; 26 | 27 | pre 28 | background-color: hsl(0, 0%, 99%); 29 | border-radius: 3px; 30 | 31 | .modal-body 32 | max-height: calc(100vh - 190px); 33 | overflow-y: scroll; 34 | 35 | 36 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 37 | -------------------------------------------------------------------------------- /client/app-more/editor/PageRole.styl: -------------------------------------------------------------------------------- 1 | 2 | .esTopicType_dropdown 3 | [class^=icon-]:before 4 | margin-left: 0; 5 | 6 | .icon-chat:before 7 | top: 1px; // instead of 2px 8 | 9 | .esTopicType 10 | .esDropModal_header 11 | margin: 0; 12 | padding: 14px 0 6px 11px; 13 | background: hsl(0, 0%, 93%); 14 | color: #111; 15 | 16 | [class^=icon-]:before 17 | margin: 0 4px 4px 0; 18 | position: relative; 19 | left: -1px; 20 | color: #333; 21 | 22 | .icon-attention-circled, 23 | .icon-help-circled 24 | &:before 25 | color: #666; 26 | 27 | 28 | -------------------------------------------------------------------------------- /client/app-more/forum/edit-intro-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .esEditIntroDlg 3 | padding-top: 2em; 4 | padding-left: 2em; 5 | 6 | p .icon-cancel 7 | margin: 0 0.5ex 0 0.1ex; 8 | whiteSpace: nowrap; 9 | color: hsl($uiHue, 50%, 30%); 10 | &:before 11 | opacity: 0.7; 12 | 13 | .form-group 14 | margin: 25px 0 0; 15 | button 16 | min-width: 160px; 17 | -------------------------------------------------------------------------------- /client/app-more/login/new-password-input.styl: -------------------------------------------------------------------------------- 1 | .s_Pw .help-block 2 | display: block; 3 | 4 | .s_Pw_Strength 5 | margin-bottom: 4px; 6 | 7 | .s_Pw_Strength_Lbl 8 | color: hsl(0, 0%, 15%); 9 | 10 | .s_Pw_Strength_Border 11 | width: 100px; 12 | height: 14px; 13 | display: inline-block; 14 | border: 1px solid hsl(0, 0%, 80%); 15 | vertical-align: middle; 16 | margin-left: 6px; 17 | position: relative; 18 | top: -1px; 19 | 20 | .s_Pw_Strength_Fill 21 | background: hsl(120, 100%, 67%); 22 | height: 100%; 23 | 24 | .s_Pw_Strength-TooWeak 25 | .s_Pw_Strength_Fill 26 | background: hsl(0, 100%, 70%); 27 | 28 | .s_Pw_Strength-FairlyWeak 29 | .s_Pw_Strength_Fill 30 | background: hsl(40, 100%, 60%); 31 | 32 | .s_Pw_Strength-OkWeak 33 | .s_Pw_Strength_Fill 34 | background: hsl(65, 100%, 48.5%) 35 | 36 | .s_Pw_BadWordTips, 37 | .s_Pw_StrongerTips 38 | color: hsl(30, 100%, 41%); 39 | 40 | 41 | -------------------------------------------------------------------------------- /client/app-more/more-prelude.more.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// -------------------------------------------------------------------------------- /client/app-more/morekit/proxy-diag.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | // ProxyDialog 4 | .c_PrxyD 5 | .esDropModal_content 6 | padding: 30px 20px 20px 20px; 7 | .esDropModal_header 8 | margin: 7px 0 7px 11px; // less margin-top – already 30px padding-top, see above 9 | 10 | .c_PrxyD-Drpd 11 | // The dropdown items have their own padding, so, could use less padding-left, 12 | // but this looks better? 13 | .esDropModal_content 14 | padding: 15px 15px 18px; 15 | 16 | // Dialog buttons 17 | .c_DlgBtns 18 | float: right; 19 | button 20 | margin-left: 0.7ex; 21 | -------------------------------------------------------------------------------- /client/app-more/notification/notf-prefs-dropdown.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_NotfPrefDD_WhyInh 3 | font-style: italic; 4 | color: #666; 5 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/ChangePageModal.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | .s_ChPgD .esDropModal_content 4 | padding-top: 12px; 5 | padding-bottom: 19px; 6 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/about-user-dialog.styl: -------------------------------------------------------------------------------- 1 | .esDropModal_content.s_InvD, 2 | .esDropModal_content.esUsrDlg 3 | padding: 0; // already padding in .modal-body 4 | width: 590px; // there's a dropdown-modal max width already 5 | 6 | .modal-body 7 | clearfix(); 8 | padding-bottom: 0; 9 | 10 | .edAvtr 11 | position: static; 12 | margin-right: 19px; 13 | $size = 110px; 14 | width: $size; 15 | height: $size; 16 | img 17 | width: 100%; 18 | height: 100%; 19 | 20 | &.esAvtr-ltr 21 | line-height: $size; 22 | font-size: $size * 0.7; 23 | // COULD reduse font-size if > 1 letter 24 | 25 | .s_UD_ExtrInf 26 | font-style: italic; 27 | margin-bottom: 1ex; 28 | 29 | .dw-about-user-actions 30 | float: right; 31 | 32 | .btn 33 | display: block; 34 | width: 170px; 35 | margin-bottom: 5px; 36 | 37 | 38 | .dw-guest-blocked 39 | color: hsl(0, 70%, 50%); 40 | font-weight: bold; 41 | 42 | .s_UD_BelwAv 43 | clear: left; 44 | padding-top: 11px; 45 | line-height: 21px; -------------------------------------------------------------------------------- /client/app-more/page-dialogs/delete-post-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .dw-delete-post-dialog 3 | input[type="button"] 4 | max-width: 150px; 5 | 6 | input[type="checkbox"] 7 | margin-top: 2px; 8 | 9 | // Sometimes there's no "Delete replies too?" question, then two borders with nothing 10 | // between just looks weird. 11 | .modal-header 12 | border-bottom: none; 13 | .modal-footer 14 | border-top: none; 15 | 16 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/move-post-dialog.styl: -------------------------------------------------------------------------------- 1 | .s_MPD_OtrSct 2 | margin-bottom: 25px; 3 | .btn 4 | margin-right: 0.6ex; 5 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/see-wrench-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .fake-tools-button 3 | margin: 0 7px; 4 | color: hsl(0, 0%, 35%); 5 | cursor: default; 6 | font-size: 115%; 7 | &:hover 8 | text-decoration: none; 9 | color: hsl(0, 0%, 35%); 10 | 11 | 12 | .esSeeWrenchDlg .dw-a-edit.icon-edit 13 | float: none; 14 | font-size: 18px; 15 | &:before 16 | color: hsl($uiHue, 45%, 45%); 17 | 18 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/snooze-notfs-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_SnzD_9amBs .btn 3 | margin: 0 9px 15px 0; 4 | grayButtonColors(); 5 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/tags-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | .esTsD .esDropModal_content 4 | padding: 9px !important; 5 | width: 900px; 6 | max-width: calc(100% - 30px); 7 | min-height: 140px; 8 | 9 | .esTsD_CreateTs 10 | .help-block 11 | margin-bottom: 5px; 12 | 13 | .btn 14 | margin-top: 10px; 15 | 16 | .form-group 17 | margin: 20px 0 0; 18 | 19 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/view-as-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | .s_VAD_Intro 4 | margin-bottom: 20px; 5 | 6 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/votes-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_VotesD .esCloseCross 3 | // Otherwise might overlap the rightmost voter's avatar. 4 | padding-bottom: 12px !important; 5 | 6 | .s_VotesD_Title 7 | // A large margin-right, so some space for the close-dialog X. 8 | margin: 4px 50px 13px 10px; 9 | 10 | .s_VotesD_Voters 11 | clearfix(); 12 | margin: 0 15px 5px 8px; 13 | max-width: 750px; 14 | .edAvtr 15 | position: static; // COULD instead remove default position: absolute; 16 | margin: 3px; 17 | 18 | -------------------------------------------------------------------------------- /client/app-more/page-dialogs/wikify-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .dw-wikify-dialog 3 | .dw-wikify-btns 4 | .form-group 5 | max-width: 500px; 6 | 7 | .form-control 8 | width: auto; 9 | min-width: 210px; 10 | -------------------------------------------------------------------------------- /client/app-more/page-tools/page-tools.styl: -------------------------------------------------------------------------------- 1 | 2 | .esPinnedOk 3 | max-width: 480px; 4 | 5 | .icon-pin:before 6 | font-size: 23px; 7 | color: hsl(0, 0%, 35%); 8 | 9 | 10 | .s_PageIdsD 11 | max-width: calc(100% - 30px); 12 | width: 720px; 13 | .input-group 14 | width: calc(100% - 32px); 15 | textarea 16 | min-height: 100px; 17 | white-space: nowrap; 18 | 19 | a code 20 | color: hsl($uiHue, 100%, 35%); 21 | text-decoration: underline; 22 | text-decoration-color: hsl($uiHue, 100%, 70%); -------------------------------------------------------------------------------- /client/app-more/tags/bookmarks-dropdown.styl: -------------------------------------------------------------------------------- 1 | 2 | .c_BokmDrpdD 3 | .esDropModal_content 4 | width: 550px; 5 | 6 | // Too mutch whitespace — the dialog is mostly empty. 7 | .modal-header 8 | padding: 10px 10px 0; 9 | border-bottom: none; 10 | 11 | .modal-body 12 | padding: 20px 10px 0px; 13 | 14 | .modal-footer 15 | border-top: none; 16 | padding-right: 10px; 17 | padding-bottom: 0; 18 | 19 | .input-group 20 | width: 100%; 21 | 22 | // Just for now. [red_delete_btn] 23 | .modal-footer .btn-delete 24 | margin-left: 2em !important; // hmm 25 | margin-right: 2em; 26 | color: hsl(0, 30%, 20%); 27 | border-color: hsl(0, 30%, 74%); 28 | // The trash icon feels a bit like whitespace, so need less. 29 | padding-left: 6px; // else 12px from .btn 30 | 31 | -------------------------------------------------------------------------------- /client/app-more/tags/tag-dropdown.styl: -------------------------------------------------------------------------------- 1 | 2 | .c_TagDrpdD 3 | .esDropModal_header 4 | font-style: normal; // italics (the default) can make the tag name harder to read 5 | margin-bottom: 8px; // a bit more, looks better. 6 | samp 7 | margin-left: 0.5ex; // space before the tag name 8 | -------------------------------------------------------------------------------- /client/app-more/tags/tags.more.styl: -------------------------------------------------------------------------------- 1 | 2 | .c_Tag_EdF-IsEd 3 | padding-left: 20px; 4 | 5 | .c_Tag_UrlSlug code 6 | margin-left: 0.8ex; 7 | 8 | .c_Tag_ValTyp 9 | margin-bottom: 1em; 10 | .form-group 11 | padding-left: 4ex; 12 | .radio 13 | margin-bottom: 5px; 14 | 15 | .c_Tag_ValTyp_CanAns 16 | margin-left: 0.8ex; 17 | 18 | .c_Tag_Ids 19 | code, i 20 | margin-left: 0.8ex; 21 | 22 | .c_Tag_Ids_Id 23 | // Internal ids are less interesting (so, smaller font). 24 | font-size: 87%; 25 | color: hsl(0 0% 31%); 26 | 27 | 28 | .c_Tag_EdBtns 29 | margin-bottom: 0.8em; 30 | .btn 31 | margin-right: 1.2ex; 32 | 33 | .c_Tag_EdF-IsEd .c_Tag_EdBtns 34 | // More margin so it's simpler to see where the editable fields (above) ends, 35 | // and that there's sth else below (recently tagged posts). 36 | margin: 1.8em 0 4em; 37 | 38 | .c_SavInf 39 | display: inline-block; 40 | margin-left: 1ex; 41 | 42 | .c_Tag_SchLn 43 | a 44 | margin-right: 0.8ex; 45 | -------------------------------------------------------------------------------- /client/app-more/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | "types": ["react", "react-dom", "lodash", "core-js"], 7 | "outFile": "more-typescript.js", 8 | "sourceMap": true, 9 | "inlineSources": true // include source code in mapping file 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | ], 14 | "exclude": [ 15 | "more-bundle-already-loaded.d.ts", 16 | "**/*.spec.ts", 17 | "**/*.test.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /client/app-more/users/group-members.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | .s_G_Mbrs 4 | margin-top: 22px; 5 | 6 | li 7 | list-style: none; 8 | margin: 0 0 12px; 9 | -------------------------------------------------------------------------------- /client/app-more/users/groups-page.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | .s_GP 4 | margin-top: 22px; 5 | 6 | .s_GP h3 7 | font-size: 23px; 8 | color: #444; 9 | 10 | .s_GP_CrGB 11 | margin: 10px 0 4px; 12 | 13 | .s_GP_Expl 14 | margin-bottom: 1ex; 15 | 16 | li.s_Gs_G 17 | list-style: none; 18 | margin: 16px 0 5px; 19 | 20 | .esP_By_F, 21 | .esP_By_U, 22 | .s_Gs_G_Stats 23 | font-size: 15px; 24 | 25 | .esP_By_F 26 | color: hsl(0, 0%, 20%); // or too dark 27 | .s_Gs_G .esP_By_U 28 | color: hsl(0, 0%, 10%); 29 | 30 | .s_Gs_G_Stats 31 | margin-top: 3px; 32 | color: hsl(0, 0%, 29%); 33 | 34 | 35 | .s_CrGD .input-group 36 | width: 300px; 37 | max-width: 90%; 38 | -------------------------------------------------------------------------------- /client/app-more/users/pat-perms.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_PP_PrmsTb_UplExts .input-group 3 | width: 100%; 4 | max-width: 720px; 5 | padding-right: 20px; 6 | textarea 7 | font-family: monospace; -------------------------------------------------------------------------------- /client/app-more/users/user-invites.styl: -------------------------------------------------------------------------------- 1 | 2 | // Otherwise the email input field is annoyingly narrow. 3 | .s_InvD .input-group 4 | width: calc(100% - 30px); 5 | 6 | // ... and annoyingly short. 7 | .s_InvD textarea 8 | height: 150px; 9 | 10 | .s_InvsL_It-Dd td 11 | text-decoration: line-through; 12 | text-decoration-color: #555; 13 | 14 | -------------------------------------------------------------------------------- /client/app-more/users/user-prefs.styl: -------------------------------------------------------------------------------- 1 | 2 | .c_PrivPrefsF 3 | .form-group p 4 | margin-bottom: 0.5ex; 5 | 6 | -------------------------------------------------------------------------------- /client/app-more/util/stupid-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .modal-dialog.esStupidDlg-Large 3 | width: calc(100% - 100px); 4 | max-width: 1550px; 5 | 6 | // Fill whole screen, on small screens. 7 | @media (max-width: 700px) 8 | width: auto; 9 | position: absolute; 10 | top: 2px; 11 | left: 2px; 12 | right: 2px; 13 | // bottom — don't set to 0 — would that cause problems if it needs 14 | // to be taller? (with an y srollbar) 15 | 16 | .esStupidDlg-Small 17 | max-width: 480px; 18 | 19 | .esStupidDlg-Tiny 20 | max-width: 350px; 21 | 22 | .esStupidDlg-BgFnt .modal-body 23 | div, p 24 | font-size: 18px; 25 | 26 | .esStupidDlg .btn 27 | margin-left: 7px; 28 | min-width: 84px; 29 | &:first-child 30 | margin-left: 0; 31 | 32 | -------------------------------------------------------------------------------- /client/app-more/utils/PatternInput.styl: -------------------------------------------------------------------------------- 1 | .s_PatInp_Err 2 | color: $errorTextColorNoBg; 3 | font-weight: bold; 4 | -------------------------------------------------------------------------------- /client/app-slim/call-start-stuff.js: -------------------------------------------------------------------------------- 1 | debiki.startStuff(); 2 | -------------------------------------------------------------------------------- /client/app-slim/if-in-iframe.styl: -------------------------------------------------------------------------------- 1 | 2 | // COULD_OPTIMIZE later: skip rendering completely, but for now, quick fix: 3 | html.s_InIframe 4 | .esPageHeader, 5 | .s_Tb, 6 | .dw-p-ttl, 7 | .dw-ar-p-hd, 8 | .dw-ar-p, 9 | footer 10 | display: none; 11 | 12 | #esPageScrollable 13 | overflow: hidden; // otherwise a not-needed? scrollbar-y appears, for some weird reason 14 | 15 | #dwPosts > div > .container 16 | padding: 0; 17 | 18 | .dw-p-as.dw-as.esPA 19 | margin-top: 0; 20 | -------------------------------------------------------------------------------- /client/app-slim/old/unused.styl: -------------------------------------------------------------------------------- 1 | /* 2 | // SVG 3 | //=========================== 4 | 5 | 6 | #dw-t-root 7 | overflow: hidden // hides the excessively wide elems, see above 8 | 9 | .dw-flip-hz 10 | -moz-transform: scaleX(-1) 11 | -o-transform: scaleX(-1) 12 | -webkit-transform: scaleX(-1) 13 | transform: scaleX(-1) 14 | filter: FlipH 15 | -ms-filter: "FlipH" 16 | 17 | 18 | 19 | // Dragsorting 20 | //=========================== 21 | 22 | // A
with this class is appended to an element that's being dragsorted, 23 | // so it's totally obvious what is being dragsorted. 24 | .dw-dragsort-shadow 25 | position: absolute 26 | width: 100% 27 | height: 100% 28 | z-index: 10 29 | background: hsl(0, 0%, 50%) 30 | top: 0 31 | opacity: 0.3 32 | 33 | // When dragsorting pinned posts: 34 | &.dw-dragsort-pin-vt 35 | // There's some whitespace below where the actions are shown. Looks better if 36 | // the shadow doesn't cover all that whitespace, but instead extends a bit above 37 | // the tree. 38 | top: -20px 39 | 40 | */ 41 | -------------------------------------------------------------------------------- /client/app-slim/page-dialogs/server-error-dialog.styl: -------------------------------------------------------------------------------- 1 | 2 | .dw-server-error .modal-dialog 3 | width: calc(100% - 50px); 4 | 5 | @media (min-width: 1150px) 6 | .dw-server-error .modal-dialog 7 | width: calc(100% - 100px); 8 | 9 | 10 | .dw-server-error .modal-dialog 11 | user-select: text; 12 | 13 | .s_SED_Msg 14 | white-space: pre-wrap; 15 | font-family: monospace; 16 | 17 | 18 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 19 | -------------------------------------------------------------------------------- /client/app-slim/page/social-buttons.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_LikeBs 3 | float: left; 4 | clear: both; 5 | margin: 30px 0 0; // v-aligns with the PostAction buttons [7PWTC42Y] 6 | 7 | [id^=twitter-widget-], 8 | .google-plus, 9 | .fb-like 10 | float: left; 11 | 12 | .google-plus, 13 | .fb-like 14 | margin-left: 12px; 15 | line-height: 0; // otherwise the G+ button moves down a bit -------------------------------------------------------------------------------- /client/app-slim/plugins.styl: -------------------------------------------------------------------------------- 1 | 2 | .DW.DW .s_UtxNextTask // [plugin] 3 | background-color: hsl($uiHue, 87%, 94%); 4 | max-width: none; 5 | margin: 0 0 15px; 6 | padding: 0 13px; 7 | .s_UtxHelp_HaveAsked_ContinueB 8 | margin: 18px auto 27px; 9 | padding: 10px 0; 10 | -------------------------------------------------------------------------------- /client/app-slim/plugins/usability-testing-exchange.styl: -------------------------------------------------------------------------------- 1 | 2 | // [plugin] this whole file. 3 | 4 | .s_UtxHelp_HaveAsked 5 | p 6 | line-height: 21px; 7 | font-size: 14px; 8 | 9 | .s_UtxHelp_HaveAsked_Title 10 | font-weight: bold; 11 | font-size: 21px; 12 | margin: 5px 0 10px; 13 | display: block; 14 | line-height: 21px; 15 | 16 | .s_UtxHelp_HaveAsked_ContinueB-skip 17 | box-shadow: 2px 4px 10px #8f8f8f; 18 | font-size: 14px; 19 | display: block; 20 | width: 145px; 21 | margin: 24px auto 23px; 22 | color: #333; 23 | background: #fff; 24 | border: none; 25 | &:hover 26 | background-color: hsl($uiHue, 88%, 86%); 27 | color: hsl($uiHue, 100%, 13%); 28 | 29 | .s_UtxHelp_HaveAsked_ContinueB 30 | box-shadow: 2px 4px 10px hsl(0, 0%, 56%); 31 | padding: 11px 0; 32 | font-size: 21px; 33 | font-weight: bold; 34 | display: block; 35 | width: 255px; 36 | margin: 30px auto 30px; 37 | -------------------------------------------------------------------------------- /client/app-slim/server-vars.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare const talkyard: TalkyardApi; 4 | 5 | // REMOVE? shouldn't access, if in emb cmts editor or login popup, 6 | // instead, should use getMainWin().typs. 7 | declare const typs: PageSession; 8 | 9 | declare const eds: ServerVars; // RENAME to tys ? And is there any way to make all fields 'const' ? 10 | 11 | // Old: 12 | declare const debiki: any; 13 | -------------------------------------------------------------------------------- /client/app-slim/staff-bundle-not-yet-loaded.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | 5 | namespace debiki2.staffbundle { 6 | 7 | export function loadAdminGuide(handler: (adminGuide: RElm) => V) { 8 | Server.loadStaffScriptsBundle(() => { 9 | handler(debiki2.admin.AdminGuide); 10 | }) 11 | } 12 | 13 | export function loadStaffTours(handler: (tours: StaffTours) => V) { 14 | Server.loadStaffScriptsBundle(() => { 15 | handler(debiki2.admin['staffTours']); 16 | }) 17 | } 18 | 19 | } 20 | 21 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 22 | -------------------------------------------------------------------------------- /client/app-slim/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | "types": ["react", "react-dom", "lodash", "core-js"], 7 | "outFile": "slim-typescript.js", 8 | "sourceMap": true, 9 | "inlineSources": true // include source code in mapping file 10 | }, 11 | "include": [ 12 | "**/*.ts" 13 | ], 14 | "exclude": [ 15 | "slim-bundle.d.ts", 16 | "**/*.spec.ts", 17 | "**/*.test.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /client/app-slim/util/FadingBackdrop.styl: -------------------------------------------------------------------------------- 1 | 2 | .esFadingBackdrop 3 | position: fixed; 4 | top: 0; 5 | right: 0; 6 | bottom: 0; 7 | left: 0; 8 | background: black; // but js sets opacity. 9 | pointer-events: none; 10 | -------------------------------------------------------------------------------- /client/app-slim/util/resizable.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_Resizing, 3 | .s_Resizor-Up 4 | user-select: none; 5 | -moz-user-select: none; 6 | -webkit-user-select: none; 7 | -ms-user-select: none; 8 | 9 | .s_Resizor-Up 10 | position: relative; 11 | height: 18px; 12 | top: -6px; 13 | cursor: n-resize; 14 | 15 | -------------------------------------------------------------------------------- /client/app-slim/utils/close-cross.styl: -------------------------------------------------------------------------------- 1 | 2 | .esCloseCross 3 | padding: 6px 11px; 4 | border: none; 5 | background: none; 6 | font-size: 23px; 7 | line-height: 23px; 8 | color: hsl(0, 0%, 35%); 9 | &:before 10 | content: '×'; // is the × U+00D7 multiplication sign. 11 | &:hover 12 | color: black; 13 | font-weight: bold; 14 | cursor: pointer; 15 | 16 | -------------------------------------------------------------------------------- /client/app-slim/utils/css-popup.styl: -------------------------------------------------------------------------------- 1 | 2 | .esCssPopup 3 | position: fixed; 4 | z-index: 1; 5 | top: 0; 6 | bottom: 0; 7 | left: 0; 8 | right: 0; 9 | background: rgba(0, 0, 0, 0.6); 10 | transition: opacity 500ms ease-in-out; 11 | visibility: hidden; 12 | opacity: 0; 13 | 14 | .esCssPopup:target 15 | visibility: visible; 16 | opacity: 1; 17 | 18 | .esCssPopup_box 19 | margin: 20vh auto; 20 | padding: 40px 20px 30px; 21 | background: hsl(0, 0%, 97%); 22 | border-radius: 5px; 23 | position: relative; 24 | transition: all 3s ease-in-out, margin 1ms; 25 | min-width: 250px; 26 | max-width: 600px; 27 | 28 | .esCssPopup_closeBackground 29 | cursor: default; 30 | position: fixed; 31 | top: 0; bottom: 0; left: 0; right: 0; 32 | 33 | .esCssPopup_closeButton 34 | position: absolute; 35 | top: 20px; 36 | right: 25px; 37 | font-size: 30px; 38 | font-weight: bold; 39 | text-decoration: none !important; 40 | &:before 41 | content: '×'; 42 | 43 | -------------------------------------------------------------------------------- /client/app-slim/utils/fade-in-on-click.styl: -------------------------------------------------------------------------------- 1 | 2 | // DO_AFTER 2021-01-01 delete all this anim stuff. Use CSS anims instead. [REACTANIMS] 3 | 4 | // COULD rename these classes to... "fade-in--..."? 5 | 6 | .s_FadeGrowIn-enter 7 | opacity: 0.01; 8 | height: 1px; 9 | transition: opacity .6s ease-in; 10 | 11 | .s_FadeGrowIn-enter.s_FadeGrowIn-enter-active 12 | opacity: 1; 13 | height: auto; 14 | 15 | .s_FadeGrowIn-exit 16 | opacity: 1; 17 | transition: opacity .5s ease-in; 18 | 19 | .s_FadeGrowIn-exit.s_FadeGrowIn-exit-active 20 | opacity: 0.01; 21 | 22 | 23 | // vim: fdm=marker et ts=2 sw=2 tw=0 list 24 | -------------------------------------------------------------------------------- /client/app-staff/admin/api-panel.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_A_Api_SecrT 3 | margin: 1em 0 2em; 4 | td, th 5 | padding: 0.5ex 1.5em; 6 | &:first-child 7 | padding-left: 0; 8 | 9 | .btn 10 | margin: 0 1em; 11 | &:first-child 12 | margin-left: 0; 13 | 14 | 15 | .c_A_Api_Wh 16 | clearfix(); 17 | 18 | .c_A_Api_Wh_Url .input-group 19 | width: 100%; 20 | 21 | 22 | li.c_WbhkReqs_Req 23 | padding-left: 1em; 24 | margin-left: 1.2em; 25 | margin-bottom: 1.7em; 26 | 27 | .c_WbhkReqs_Req b 28 | margin-right: 0.5ex; 29 | 30 | .c_A_Api_Wh_BrknQ 31 | margin: 0 1ex 0 2ex; 32 | 33 | .c_A_Api_Wh_Msg 34 | margin: 0.3ex 0; 35 | -------------------------------------------------------------------------------- /client/app-staff/admin/hostname-editor.styl: -------------------------------------------------------------------------------- 1 | 2 | .s_A_NewAdrD_HostnI .input-group 3 | width: calc(100% - 40px); 4 | 5 | 6 | .s_A_NewAdrD:has(.s_PatInp_Err) input 7 | outline: 2px $errorOutlineBackground solid; 8 | background: $errorInputBackground; 9 | 10 | -------------------------------------------------------------------------------- /client/app-staff/misc-staff.styl: -------------------------------------------------------------------------------- 1 | .s_TrtLvTl 2 | margin-right: 0.4ex; 3 | -------------------------------------------------------------------------------- /client/app-staff/staff-bundle-already-loaded.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare namespace debiki2 { 3 | 4 | namespace admin { 5 | var staffRoutes; 6 | var AdminGuide; 7 | } 8 | 9 | namespace superadmin { 10 | var routes; 11 | } 12 | 13 | namespace createsite { 14 | var routes; 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /client/app-staff/staff-prelude.staff.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// -------------------------------------------------------------------------------- /client/app-staff/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | "types": ["react", "react-dom", "lodash", "core-js"], 7 | "outFile": "staff-typescript.js", 8 | "sourceMap": true, 9 | "inlineSources": true // include source code in mapping file 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | "../app-slim/slim-bundle.d.ts", 14 | ], 15 | "exclude": [ 16 | "staff-bundle-already-loaded.d.ts", 17 | "**/*.spec.ts", 18 | "**/*.test.ts", 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /client/embedded-comments/embedding-page.d.ts: -------------------------------------------------------------------------------- 1 | /* later [052MKHGJw3] 2 | declare global { 3 | interface window { 4 | talkyardDebug: boolean | undefined; // window 5 | edRemoveCommentsAndEditor: any; // window 6 | edReloadCommentsAndEditor: any; // window 7 | talkyardRemoveCommentsAndEditor: any; // window 8 | talkyardReloadCommentsAndEditor: any; // window 9 | } 10 | 11 | const debiki: any | undefined; 12 | const Bliss: any | undefined; 13 | declare function smoothScroll(elem: Element, x: number, y: number, 14 | durationMs?: number, onDone?: () => void); 15 | } 16 | 17 | export {} 18 | */ -------------------------------------------------------------------------------- /client/embedded-comments/parent-footer.js: -------------------------------------------------------------------------------- 1 | 2 | // This file is appended to the embedded comments script bundle, by gulpfile.js. 3 | // See readme.txt. In parent-header.js, we ended with: `(function() {` and here we finish with: 4 | 5 | })(); 6 | 7 | // vim: et sw=2 ts=2 tw=0 list 8 | -------------------------------------------------------------------------------- /client/embedded-comments/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | gulpfile.js concatenates these files: 3 | - parent-header.js 4 | - some other files, including iframe-parent.js 5 | - parent-footer.js 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/embedded-comments/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | "types": ["core-js"], 7 | "outFile": "blog-comments-typescript.js", 8 | "sourceMap": true, 9 | "inlineSources": true // include source code in mapping file 10 | }, 11 | "include": [ 12 | "**/*.ts" 13 | ], 14 | "exclude": [ 15 | "**/*.spec.ts", 16 | "**/*.test.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /client/macros/macros-none.h: -------------------------------------------------------------------------------- 1 | // This file is just used to verify that the C Preprocessor won't 2 | // mess up anything — if preprocessing a file with no matching macros, 3 | // the file shouldn't get changed. 4 | 5 | #define __DUMMY_NOOP_MACRO__ "was_macro: __DUMMY_NOOP_MACRO__" 6 | -------------------------------------------------------------------------------- /client/macros/macros-prod.h: -------------------------------------------------------------------------------- 1 | // Find docs in ./macros-dev.h. 2 | 3 | // Define the macros to 'void 0' i.e. undefined, which is what the function calls 4 | // return in dev builds — so the prod build behaves in the same way, except 5 | // that it won't log / do anything. 6 | 7 | #define D_DO(fn) void 0 8 | #define D_LOG_T(msg) void 0 9 | #define D_LOG_D(msg) void 0 10 | #define D_LOG_M(msg) void 0 11 | #define D_LOG_W(msg) void 0 12 | #define D_LOG_W2(msg, ex) void 0 13 | #define D_LOG_E(msg) void 0 14 | #define D_LOG_E2(msg, ex) void 0 15 | #define D_DIE(errMsg) void 0 16 | #define D_DIE_IF(test, errMsg) void 0 17 | -------------------------------------------------------------------------------- /client/macros/macros.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // These macros get changed to: dev_build__do/logT/logD/logM(..) etc in dev builds, 5 | // but in prod builds, get changed to: void 0 = undefined, and removed by the minifier. 6 | declare function D_DO(fn); 7 | declare function D_LOG_T(msg: St); 8 | declare function D_LOG_D(msg: St); 9 | declare function D_LOG_M(msg: St); 10 | declare function D_LOG_W(msg: St); 11 | declare function D_LOG_W2(msg: St, ex); 12 | declare function D_LOG_E(msg: St); 13 | declare function D_LOG_E2(msg: St, ex); 14 | declare function D_DIE(errMsg: St); 15 | declare function D_DIE_IF(test: Bo, errMsg: St); -------------------------------------------------------------------------------- /client/reactjs-types.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // JSX.Element in node_modules/@types/react/index.d.ts: 4 | // 5 | // interface Element extends React.ReactElement { } 6 | // 7 | type RElm = JSX.Element; 8 | 9 | // Also: type HElm = HTMLElement; 10 | 11 | 12 | // JSX.ElementClass in node_modules/@types/react/index.d.ts: 13 | // 14 | // interface ElementClass extends React.Component { 15 | // render(): React.ReactNode; 16 | // } 17 | // 18 | type RElmClass = JSX.ElementClass; 19 | -------------------------------------------------------------------------------- /client/rtl/bootstrap.styl: -------------------------------------------------------------------------------- 1 | ../../node_modules/bootstrap/dist/css/bootstrap.css -------------------------------------------------------------------------------- /client/rtl/react-select.styl: -------------------------------------------------------------------------------- 1 | ../../node_modules/react-select/dist/react-select.css -------------------------------------------------------------------------------- /client/rtl/react-textarea-autocomplete.styl: -------------------------------------------------------------------------------- 1 | ../../node_modules/@webscopeio/react-textarea-autocomplete/style.css -------------------------------------------------------------------------------- /client/server/ReactActions.ts: -------------------------------------------------------------------------------- 1 | // This avoids 'Cannot read property "..." from undefined' errors. 2 | 3 | module debiki2 { 4 | 5 | export var ReactActions: any = {}; 6 | 7 | } 8 | 9 | // vim: fdm=marker et ts=2 sw=2 tw=0 list 10 | -------------------------------------------------------------------------------- /client/server/ReactStore.ts: -------------------------------------------------------------------------------- 1 | // An implementation is provided in app/debiki/ReactRenderer.scala for server side rendering. 2 | 3 | 4 | //------------------------------------------------------------------------------ 5 | module debiki2 { 6 | //------------------------------------------------------------------------------ 7 | 8 | export function win_getSessWinStore(): SessWinStore { 9 | return ( window).theStore; // [ONESTORE] 10 | } 11 | 12 | export function makeNoPageData() { die('K42B01'); } 13 | export function makeAutoPage(pageId?: PageId): any { die('K42B02'); } 14 | 15 | //------------------------------------------------------------------------------ 16 | } 17 | //------------------------------------------------------------------------------ 18 | // vim: fdm=marker et ts=2 sw=2 tw=0 list 19 | -------------------------------------------------------------------------------- /client/server/Server.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2 { 3 | 4 | export var Server: any = { 5 | logError: function(errorMessage: string) { 6 | console.error(errorMessage); 7 | } 8 | }; 9 | 10 | } 11 | 12 | -------------------------------------------------------------------------------- /client/server/ServerApi.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/ServerApi.ts -------------------------------------------------------------------------------- /client/server/avatar/AvatarAndName.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/avatar/AvatarAndName.ts -------------------------------------------------------------------------------- /client/server/avatar/avatar.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/avatar/avatar.ts -------------------------------------------------------------------------------- /client/server/constants.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/constants.ts -------------------------------------------------------------------------------- /client/server/edit-history/edit-history-dialog.ts: -------------------------------------------------------------------------------- 1 | // This makes the TypeScript compiler happy when it bundles server side code. 2 | 3 | module debiki2 { 4 | export var edithistory: any = {}; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /client/server/editor-bundle-not-yet-loaded.ts: -------------------------------------------------------------------------------- 1 | // This makes the TypeScript compiler happy when it bundles server side code. 2 | 3 | declare namespace debiki2 { 4 | export const editor: any; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /client/server/editor/CdnLinkifyer.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/editor/CdnLinkifyer.ts -------------------------------------------------------------------------------- /client/server/editor/link-previews-markdown-it-plugin.editor.ts: -------------------------------------------------------------------------------- 1 | ../../app-editor/editor/link-previews-markdown-it-plugin.editor.ts -------------------------------------------------------------------------------- /client/server/editor/mentions-markdown-it-plugin.ts: -------------------------------------------------------------------------------- 1 | ../../app-editor/editor/mentions-markdown-it-plugin.ts -------------------------------------------------------------------------------- /client/server/editor/title-editor.ts: -------------------------------------------------------------------------------- 1 | // This makes the TypeScript compiler happy when it bundles server side code. 2 | 3 | module debiki2.titleeditor { 4 | export var TitleEditor: any = {}; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /client/server/forum/edit-intro-dialog.ts: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | module debiki2.forum { 3 | //------------------------------------------------------------------------------ 4 | 5 | export function openEditIntroDialog() { 6 | } 7 | 8 | //------------------------------------------------------------------------------ 9 | } 10 | //------------------------------------------------------------------------------ 11 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 12 | -------------------------------------------------------------------------------- /client/server/forum/forum.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/forum/forum.ts -------------------------------------------------------------------------------- /client/server/help/help.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/help/help.ts -------------------------------------------------------------------------------- /client/server/help/serverAnnouncements.ts: -------------------------------------------------------------------------------- 1 | 2 | //------------------------------------------------------------------------------ 3 | namespace debiki2.help { 4 | //------------------------------------------------------------------------------ 5 | 6 | export function getServerAnnouncements(store: Store): RElm | Nl { 7 | return null; 8 | } 9 | 10 | export function anyMaintMsg(ps: { brief?: true } = {}): RElm | N { 11 | return null; 12 | } 13 | 14 | //------------------------------------------------------------------------------ 15 | } 16 | //------------------------------------------------------------------------------ 17 | // vim: fdm=marker et ts=2 sw=2 tw=0 list 18 | -------------------------------------------------------------------------------- /client/server/links.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/links.ts -------------------------------------------------------------------------------- /client/server/login/login-dialog.ts: -------------------------------------------------------------------------------- 1 | module debiki2.login { 2 | export function getLoginDialog(): any {} 3 | export function loginIfNeeded(...whatever): any {} 4 | } 5 | -------------------------------------------------------------------------------- /client/server/login/login-if-needed.ts: -------------------------------------------------------------------------------- 1 | 2 | //------------------------------------------------------------------------------ 3 | namespace debiki2.login { 4 | //------------------------------------------------------------------------------ 5 | 6 | export function loginIfNeededReturnToPost( 7 | loginReason: LoginReason | string, postNr: PostNr, success?: () => void, willCompose?: boolean) { 8 | } 9 | 10 | 11 | export function loginIfNeededReturnToAnchor( 12 | loginReason: LoginReason | string, anchor: string, success?: () => void, willCompose?: boolean) { 13 | } 14 | 15 | 16 | export function openLoginDialogToSignUp(purpose) { 17 | } 18 | 19 | 20 | export function openLoginDialog(purpose) { 21 | } 22 | 23 | //------------------------------------------------------------------------------ 24 | } 25 | //------------------------------------------------------------------------------ 26 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 27 | -------------------------------------------------------------------------------- /client/server/login/login.ts: -------------------------------------------------------------------------------- 1 | module debiki2.login { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /client/server/magic-time.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | //------------------------------------------------------------------------------ 4 | namespace debiki2 { 5 | //------------------------------------------------------------------------------ 6 | 7 | export function addTestExtraMillis() { 8 | } 9 | 10 | export function getNowMs(): WhenMs { 11 | return Date.now(); 12 | } 13 | 14 | export function magicTimeout(millis: number, callback: () => void) { 15 | } 16 | 17 | export let magicIntervalHandle; 18 | 19 | export function startMagicTime(anyStartTimeMs?: number) { 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | } 24 | //------------------------------------------------------------------------------ 25 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 26 | -------------------------------------------------------------------------------- /client/server/me-getters.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/me-getters.ts -------------------------------------------------------------------------------- /client/server/model.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/model.ts -------------------------------------------------------------------------------- /client/server/more-bundle-not-yet-loaded.ts: -------------------------------------------------------------------------------- 1 | // This makes the TypeScript compiler happy when it bundles server side code. 2 | 3 | declare namespace debiki2 { 4 | export const morebundle: any; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /client/server/notification/Notification.ts: -------------------------------------------------------------------------------- 1 | // This avoids 'Cannot read property "..." from undefined' errors. 2 | 3 | module debiki2.notification { 4 | 5 | export var Notification: any = {}; 6 | 7 | } 8 | 9 | // vim: fdm=marker et ts=2 sw=2 tw=0 list 10 | -------------------------------------------------------------------------------- /client/server/oop-methods.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/oop-methods.ts -------------------------------------------------------------------------------- /client/server/page-dialogs/delete-post-dialog.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2.pagedialogs { 3 | export function getDeletePostDialog(): any {} 4 | } 5 | 6 | -------------------------------------------------------------------------------- /client/server/page-dialogs/flag-dialog.ts: -------------------------------------------------------------------------------- 1 | module debiki2.pagedialogs { 2 | export function openFlagDialog(postId: PostId): any {} 3 | } 4 | 5 | module debiki2.pagedialogs { 6 | export function getAboutUserDialog(): any {} 7 | } 8 | -------------------------------------------------------------------------------- /client/server/page-dialogs/move-posts-dialog.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2.pagedialogs { 3 | export function openMovePostsDialog(...whatever): any {} 4 | } 5 | -------------------------------------------------------------------------------- /client/server/page-dialogs/open-share-popup.ts: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | namespace debiki2.pagedialogs { 3 | //------------------------------------------------------------------------------ 4 | 5 | 6 | export const Facebook = 'facebook'; 7 | export const Twitter = 'twitter'; 8 | export const Google = 'google'; 9 | export const Email = 'mail'; 10 | export const LinkedIn = 'linkedin'; 11 | 12 | 13 | export function openSharePopup(url: string, where: string) { 14 | } 15 | 16 | 17 | //------------------------------------------------------------------------------ 18 | } 19 | //------------------------------------------------------------------------------ 20 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=tcqwn list 21 | -------------------------------------------------------------------------------- /client/server/page-dialogs/see-wrench-dialog.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2.pagedialogs { 3 | export function openSeeWrenchDialog(): any {} 4 | } 5 | 6 | -------------------------------------------------------------------------------- /client/server/page-dialogs/share-dialog.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2.pagedialogs { 3 | export function openShareDialog(...whatever): any {} 4 | } 5 | 6 | -------------------------------------------------------------------------------- /client/server/page-dialogs/tags-dialog.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2.pagedialogs { 3 | export function openTagsDialog(...whatever): any {} 4 | } 5 | -------------------------------------------------------------------------------- /client/server/page-dialogs/wikify-dialog.ts: -------------------------------------------------------------------------------- 1 | 2 | module debiki2.pagedialogs { 3 | export function getWikifyDialog(): any {} 4 | } 5 | -------------------------------------------------------------------------------- /client/server/page-methods.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/page-methods.ts -------------------------------------------------------------------------------- /client/server/page-tools/page-tools.ts: -------------------------------------------------------------------------------- 1 | module debiki2.pagetools { 2 | export function getPageToolsDialog(): any {} 3 | } 4 | 5 | -------------------------------------------------------------------------------- /client/server/page/arrows.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/arrows.ts -------------------------------------------------------------------------------- /client/server/page/cats-or-home-link.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/cats-or-home-link.ts -------------------------------------------------------------------------------- /client/server/page/chat.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/chat.ts -------------------------------------------------------------------------------- /client/server/page/discussion.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/discussion.ts -------------------------------------------------------------------------------- /client/server/page/hacks.ts: -------------------------------------------------------------------------------- 1 | 2 | //------------------------------------------------------------------------------ 3 | namespace debiki2.page.Hacks { 4 | //------------------------------------------------------------------------------ 5 | 6 | export function navigateTo(path: St): V | true { 7 | // Noop, when server side rendering. 8 | } 9 | 10 | export const ExtReactRootNavComponent = createReactClass({ 11 | render: function() { 12 | return null; 13 | } 14 | }); 15 | 16 | 17 | export function reactRouterLinkifyTopHeaderLinks() { 18 | // Noop. 19 | } 20 | 21 | 22 | export function processPosts(startElemId?: string) { 23 | // Noop. 24 | } 25 | 26 | //------------------------------------------------------------------------------ 27 | } 28 | //------------------------------------------------------------------------------ 29 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=tcqwn list 30 | -------------------------------------------------------------------------------- /client/server/page/metabar.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/metabar.ts -------------------------------------------------------------------------------- /client/server/page/page.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/page.ts -------------------------------------------------------------------------------- /client/server/page/post-actions.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/post-actions.ts -------------------------------------------------------------------------------- /client/server/page/scroll-buttons.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/scroll-buttons.ts -------------------------------------------------------------------------------- /client/server/page/social-buttons.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/page/social-buttons.ts -------------------------------------------------------------------------------- /client/server/personas/PersonaIndicator.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | //------------------------------------------------------------------------------ 4 | namespace debiki2.personas { 5 | //------------------------------------------------------------------------------ 6 | 7 | 8 | export function PersonaIndicator(ps: any): RElm | N { 9 | return null; 10 | } 11 | 12 | 13 | //------------------------------------------------------------------------------ 14 | } 15 | //------------------------------------------------------------------------------ 16 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=tcqwn list 17 | -------------------------------------------------------------------------------- /client/server/prelude.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/prelude.ts -------------------------------------------------------------------------------- /client/server/react-bootstrap-old/Input.more.ts: -------------------------------------------------------------------------------- 1 | module debiki2 { 2 | 3 | export function Input(props) { 4 | die('The Input component is used server side [EsE4FK0WY2]'); 5 | } 6 | 7 | } 8 | 9 | // vim: fdm=marker et ts=2 sw=2 tw=0 fo=r list 10 | -------------------------------------------------------------------------------- /client/server/react-elements/name-login-btns.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/react-elements/name-login-btns.ts -------------------------------------------------------------------------------- /client/server/readme.txt: -------------------------------------------------------------------------------- 1 | In this client/server/ directory, you'll find React.js components that are 2 | rendered to HTML server side, by debiki-server/app/debiki/ReactRenderer.scala. 3 | 4 | The files in here are actually softlinks to ../app/. 5 | 6 | -------------------------------------------------------------------------------- /client/server/rules.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/rules.ts -------------------------------------------------------------------------------- /client/server/server-side-type-stubs.ts: -------------------------------------------------------------------------------- 1 | // These dummy types prevent compilation errors when compiling TypeScript for 2 | // server side rendering. 3 | // 4 | // The classes listed below aren't included among the server side TypeScript files, 5 | // so unless they're declared here the TypeScript compiler complains. 6 | 7 | 8 | declare namespace debiki2 { 9 | var ReactActions: any; 10 | var ReactStore: any; 11 | var StoreListenerMixin: any; 12 | 13 | namespace utils { 14 | export const maybeRunTour: any; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /client/server/server-vars.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/server-vars.ts -------------------------------------------------------------------------------- /client/server/staff-bundle-not-yet-loaded.ts: -------------------------------------------------------------------------------- 1 | 2 | declare namespace debiki2 { 3 | export const staffbundle: any; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /client/server/store-getters.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/store-getters.ts -------------------------------------------------------------------------------- /client/server/tags/tags.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/tags/tags.ts -------------------------------------------------------------------------------- /client/server/topbar/topbar.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/topbar/topbar.ts -------------------------------------------------------------------------------- /client/server/translations.d.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/translations.d.ts -------------------------------------------------------------------------------- /client/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], 6 | // react-dom needed to compile, but isn't actually used, server side. 7 | "types": ["react", "react-dom", "lodash", "core-js", "markdown-it"], 8 | "outFile": "server-bundle.js", 9 | "sourceMap": true, 10 | "inlineSources": true // include source code in mapping file 11 | }, 12 | "include": [ 13 | "**/*.ts" 14 | ], 15 | "exclude": [ 16 | "**/*.spec.ts", 17 | "**/*.test.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /client/server/util/ExplainingDropdown.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/util/ExplainingDropdown.ts -------------------------------------------------------------------------------- /client/server/utils/DropdownModal.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/utils/DropdownModal.ts -------------------------------------------------------------------------------- /client/server/utils/page-scroll-mixin.ts: -------------------------------------------------------------------------------- 1 | module debiki2.utils { 2 | export var PageScrollMixin = {}; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /client/server/utils/react-utils.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/utils/react-utils.ts -------------------------------------------------------------------------------- /client/server/utils/show-and-highlight.ts: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | namespace debiki2 { 3 | //------------------------------------------------------------------------------ 4 | 5 | export function scrollAndFlashPostNr(...whatever): any { 6 | throw new Error('scrollAndFlashPostNr called server side [TyE502RKSG3]'); 7 | } 8 | 9 | //------------------------------------------------------------------------------ 10 | } 11 | //------------------------------------------------------------------------------ 12 | // vim: fdm=marker et ts=2 sw=2 tw=100 list 13 | -------------------------------------------------------------------------------- /client/server/utils/window-zoom-resize-mixin.ts: -------------------------------------------------------------------------------- 1 | module debiki2.utils { 2 | export var WindowZoomResizeMixin = {}; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /client/server/widgets.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/widgets.ts -------------------------------------------------------------------------------- /client/server/widgets/widget-open-buttons.ts: -------------------------------------------------------------------------------- 1 | ../../app-slim/widgets/widget-open-buttons.ts -------------------------------------------------------------------------------- /client/serviceworker/constants.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/constants.ts -------------------------------------------------------------------------------- /client/serviceworker/magic-time.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/magic-time.ts -------------------------------------------------------------------------------- /client/serviceworker/model.ts: -------------------------------------------------------------------------------- 1 | ../app-slim/model.ts -------------------------------------------------------------------------------- /client/serviceworker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "removeComments": false, 5 | "lib": ["es5", "es2015", "dom"], // dom: fetch() API related types 6 | "types": ["core-js"], 7 | "outFile": "ty-sw-typescript.js", 8 | "sourceMap": true, 9 | "inlineSources": true, // include source code in mapping file 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | ], 14 | "exclude": [ 15 | "**/*.spec.ts", 16 | "**/*.test.ts", 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /client/third-party/abbreviate-jquery.js: -------------------------------------------------------------------------------- 1 | window.$ = jQuery; 2 | window.debiki.internal.$ = $; // try to remove 3 | -------------------------------------------------------------------------------- /client/third-party/codemirror-show-markdown-line-breaks.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Makes the CodeMirror editor show newlines, in Markdown mode. 3 | * 4 | * Copyright (C) 2013 Kaj Magnus Lindberg (born 1979) 5 | * 6 | * Dual licensed under MIT and CC0. (Javascript is MIT only though.) 7 | */ 8 | 9 | 10 | .cm-markdown-single-trailing-space-odd:before, 11 | .cm-markdown-single-trailing-space-even:before, 12 | .cm-markdown-line-break:before { 13 | font-weight: bold; 14 | color: hsl(30, 100%, 50%); /* a dark orange */ 15 | position: absolute; 16 | } 17 | 18 | 19 | .cm-markdown-single-trailing-space-odd:before, 20 | .cm-markdown-single-trailing-space-even:before { 21 | content: '·'; 22 | } 23 | 24 | .cm-markdown-line-break:before { 25 | content: '↵'; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /client/third-party/diff_match_patch.js: -------------------------------------------------------------------------------- 1 | ../../modules/google-diff-match-patch/javascript/diff_match_patch.js -------------------------------------------------------------------------------- /client/third-party/popuplib.js.debiki-readme.txt: -------------------------------------------------------------------------------- 1 | popuplib.js opens a OpenID Provider login page in a popup, so we don't have to 2 | redirect the main window itself to the login page. 3 | 4 | From: http://step2.googlecode.com/svn/code/java/trunk/example-consumer/src/main/webapp/popuplib.js 5 | SVN revision: r373 6 | Date: Mar 6, 2009 7 | 8 | Currently, only a few popuplib.js helper functions are in use — Lift-Web 9 | generates a
and I use jQuery to submit it, so popuplib.js's most 10 | interesting function (?), popupManager.createPopupOpener, is not in use. 11 | — Now I commented it out. /kajmagnus 12 | 13 | Example on how to use popupManager: http://www.puffypoodles.com/popup 14 | JSP example: http://code.google.com/p/step2/source/browse/code/java/trunk/example-consumer/src/main/webapp/WEB-INF/popup.jsp?r=434 15 | 16 | -------------------------------------------------------------------------------- /client/third-party/rename-key-to-keymaster.js: -------------------------------------------------------------------------------- 1 | // keymaster.js declare a global variable window.key, but on my prod server 2 | // then very infrequently window.key is overwritten by something to ' _ga' 3 | // which results in: Uncaught TypeError: string is not a function 4 | // Prevent this, by not using windows.key, instead use windows.keymaster. 5 | if (window && window.key) { 6 | if (window.keymaster && console && console.warn) { 7 | console.warn('window.keymaster already defined, overwriting it with window.key [DwE4KEPB2]'); 8 | } 9 | window.keymaster = window.key; 10 | window.key = 'Use window.keymaster instead [DwE7VEGP8]'; 11 | 12 | 13 | // Tell KeyMaster to handle Escape clicks also inside s. 14 | window.keymaster.filter = function(event) { 15 | if (event.keyCode === 27) // escape is 27 16 | return true; 17 | var tagName = (event.target || event.originalTarget).tagName; 18 | return !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA'); 19 | }; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /client/third-party/third-party-types.d.ts: -------------------------------------------------------------------------------- 1 | 2 | interface ReactSelectV1Option { 3 | label: string; 4 | value: string | number; 5 | disabled?: boolean; 6 | } -------------------------------------------------------------------------------- /d/c: -------------------------------------------------------------------------------- 1 | sudo docker-compose $@ 2 | -------------------------------------------------------------------------------- /d/kill-down-prod-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Dupl kill-down prod test code. [KLLPRDTST] 4 | 5 | test_containers='docker-compose -p edt -f modules/ed-prod-one-test/docker-compose.yml -f modules/ed-prod-one-test/debug.yml -f modules/ed-prod-one-test-override.yml -f docker-compose-no-limits.yml' 6 | 7 | sudo $test_containers kill web app search cache rdb 8 | sudo $test_containers down 9 | -------------------------------------------------------------------------------- /d/n: -------------------------------------------------------------------------------- 1 | node -------------------------------------------------------------------------------- /d/tykc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | s/d -f docker-compose.yml -f images/keycloak/docker-compose-keycloak.yml $@ 4 | -------------------------------------------------------------------------------- /docs/CLA-history.md: -------------------------------------------------------------------------------- 1 | CLA v2 2 | --------- 3 | 4 | Apache 2: https://www.apache.org/licenses/cla-corporate.txt 5 | says: *"... prepare derivative works of, 6 | publicly display, publicly perform, sublicense, and distribute 7 | Your Contributions and such derivative works ..."* 8 | so better incl *"and such derivative works"* in Talkyard's CLA too, then? 9 | 10 | Other CLA's, e.g. Ghost's, Discourse's, don't include that, so 11 | probably not required? But let's incl, anyway. -------------------------------------------------------------------------------- /docs/anti-bugs.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | [unintened_undefined_bug] 4 | In this: `{ ...objA, ...objB }`, any fields in objA with actual values, 5 | get overwritten by any `field: undefined` fields in objB. But that's often not 6 | what was intended. 7 | Therefore, avoid setting any fields to `undefined` (unless the above is the intended 8 | behavior, but then maybe using `null` makes the intention more clear). -------------------------------------------------------------------------------- /docs/db-schema/alt_page_ids3.txt: -------------------------------------------------------------------------------- 1 | Table "public.alt_page_ids3" 2 | Column | Type | Collation | Nullable | Default 3 | --------------+-------------------+-----------+----------+--------- 4 | site_id | integer | | not null | 5 | alt_page_id | character varying | | not null | 6 | real_page_id | character varying | | not null | 7 | Indexes: 8 | "altpageids_p" PRIMARY KEY, btree (site_id, alt_page_id) 9 | Check constraints: 10 | "altpageids_altid_c_len" CHECK (length(alt_page_id::text) >= 1 AND length(alt_page_id::text) <= 300) 11 | Foreign-key constraints: 12 | "altpageids_r_pages" FOREIGN KEY (site_id, real_page_id) REFERENCES pages3(site_id, page_id) 13 | 14 | -------------------------------------------------------------------------------- /docs/db-schema/backup_test_log3.txt: -------------------------------------------------------------------------------- 1 | Table "public.backup_test_log3" 2 | Column | Type | Collation | Nullable | Default 3 | -------------------+-----------------------------+-----------+----------+--------- 4 | logged_at | timestamp without time zone | | not null | 5 | logged_by | character varying | | not null | 6 | backup_of_what | character varying | | not null | 7 | random_value | character varying | | not null | 8 | got_ok_message_at | timestamp without time zone | | | 9 | file_name | character varying | | | 10 | Indexes: 11 | "backup_test_log_i" btree (logged_at DESC) 12 | Check constraints: 13 | "backuptestlog_c_filename_len" CHECK (length(file_name::text) >= 1 AND length(file_name::text) <= 200) 14 | 15 | -------------------------------------------------------------------------------- /docs/db-schema/notices_t.txt: -------------------------------------------------------------------------------- 1 | Table "public.notices_t" 2 | Column | Type | Collation | Nullable | Default 3 | ---------------+-------------+-----------+----------+--------- 4 | site_id_c | integer | | not null | 5 | to_pat_id_c | integer | | not null | 6 | notice_id_c | i32_gz_d | | not null | 7 | first_at_c | when_mins_d | | not null | 8 | last_at_c | when_mins_d | | not null | 9 | num_total_c | i32_gz_d | | not null | 10 | notice_data_c | jsonb | | | 11 | Indexes: 12 | "notices_p_patid_noticeid" PRIMARY KEY, btree (site_id_c, to_pat_id_c, notice_id_c) 13 | "notices_ig_noticeid" btree (notice_id_c) 14 | Check constraints: 15 | "notices_c_firstat_lte_lastat" CHECK (first_at_c::integer <= last_at_c::integer) 16 | Foreign-key constraints: 17 | "notices_r_pats" FOREIGN KEY (site_id_c, to_pat_id_c) REFERENCES users3(site_id, user_id) DEFERRABLE 18 | 19 | -------------------------------------------------------------------------------- /docs/db-schema/post_tags3.txt: -------------------------------------------------------------------------------- 1 | Table "public.post_tags3" 2 | Column | Type | Collation | Nullable | Default 3 | ---------+-------------------+-----------+----------+--------- 4 | site_id | integer | | not null | 5 | post_id | integer | | not null | 6 | tag | character varying | | not null | 7 | is_page | boolean | | not null | 8 | Indexes: 9 | "posttags_site_post__p" PRIMARY KEY, btree (site_id, post_id, tag) 10 | Check constraints: 11 | "posttags_tag__c_valid" CHECK (is_valid_tag_label(tag)) 12 | Foreign-key constraints: 13 | "posttags_r_posts" FOREIGN KEY (site_id, post_id) REFERENCES posts3(site_id, unique_post_id) DEFERRABLE 14 | 15 | -------------------------------------------------------------------------------- /docs/db-schema/system_settings_t.txt: -------------------------------------------------------------------------------- 1 | Table "public.system_settings_t" 2 | Column | Type | Collation | Nullable | Default 3 | -------------------------------+-------------------------+-----------+----------+--------- 4 | maintenance_until_unix_secs_c | i64_gz_d | | | 5 | maint_words_html_unsafe_c | text_nonempty_ste500_d | | | 6 | maint_msg_html_unsafe_c | text_nonempty_ste2000_d | | | 7 | 8 | -------------------------------------------------------------------------------- /docs/db-schema/tag_notf_levels3.txt: -------------------------------------------------------------------------------- 1 | Table "public.tag_notf_levels3" 2 | Column | Type | Collation | Nullable | Default 3 | ------------+-------------------+-----------+----------+--------- 4 | site_id | integer | | not null | 5 | user_id | integer | | not null | 6 | tag | character varying | | not null | 7 | notf_level | integer | | not null | 8 | Indexes: 9 | "tagnotflvl_site_user_tag__p" PRIMARY KEY, btree (site_id, user_id, tag) 10 | Check constraints: 11 | "tagnotflvl_notf_lvl" CHECK (is_valid_notf_level(notf_level)) 12 | Foreign-key constraints: 13 | "tagnotflvl_r_people" FOREIGN KEY (site_id, user_id) REFERENCES users3(site_id, user_id) DEFERRABLE 14 | 15 | -------------------------------------------------------------------------------- /docs/safari-itp-firefox-etp.md: -------------------------------------------------------------------------------- 1 | ../modules/ty-cla/docs/safari-itp-firefox-etp.md -------------------------------------------------------------------------------- /docs/security-tests-readme.md: -------------------------------------------------------------------------------- 1 | Talkyard security tests 2 | =================================== 3 | 4 | The security tests are written in TypeScript and use Tape = test-anything-protocol for Node.js. 5 | They send requests to Nginx, so they can test the whole stack (which however wouldn't have been 6 | possible if they had been functional tests written in Scala for the app server only). 7 | 8 | 9 | Running 10 | --------------- 11 | 12 | ``` 13 | sudo s/d up -d 14 | sudo s/d-run-security-tests 15 | ``` 16 | 17 | 18 | Debugging 19 | --------------- 20 | 21 | ``` 22 | sudo s/d up -d 23 | sudo s/d-debug-security-tests 24 | ``` 25 | 26 | You'll see: 27 | 28 | > ... 29 | > To start debugging, open the following URL in Chrome: 30 | > chrome-devtools://devtools/bundled/inspector.html... 31 | > ... 32 | 33 | Do that, i.e. open that URL in Chrome or Opera. 34 | 35 | (More about debugging in Node.js 7.4 here: 36 | https://nodejs.org/api/debugger.html#debugger_v8_inspector_integration_for_node_js) 37 | -------------------------------------------------------------------------------- /docs/talkyard-api.md: -------------------------------------------------------------------------------- 1 | 2 | ``` 3 | { 4 | "ssoId": "ext2", 5 | "primaryEmailAddress": "ext12x.co", 6 | "isEmailAddressVerified": true, 7 | "username": "ext2_un", 8 | "fullName": "Ext 2 Full Name", 9 | "avatarUrl": null, 10 | "aboutUser": "About Ext 2 ..." 11 | } 12 | ``` 13 | -------------------------------------------------------------------------------- /images/app/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debiki/talkyard/f2c0a8e46b1065717a9a6645c72f905007816230/images/app/assets/.gitkeep -------------------------------------------------------------------------------- /images/backup/entrypoint.sh: -------------------------------------------------------------------------------- 1 | # Split this into an entrypoint script that calls the backup scripts? 2 | # + docker-compose.yml configuration? 3 | # 4 | # see e.g.: 5 | # https://github.com/paysera/piwik/blob/master/docker-compose.yml#L48 6 | # https://github.com/nextcloud/docker/issues/36#issuecomment-435948722 7 | backup: 8 | image: postgres:10.x.y 9 | env_file: something.env? backup.env? 10 | depends_on: 11 | - rdb # pg_dumpall connects to 12 | volumes: 13 | - same-as-rdb 14 | - same-as-uploaded-files 15 | - /etc/localtime:/etc/localtime:ro # ?? 16 | entrypoint: | 17 | # move to script 18 | bash -c 'bash -s < dump ... %d-%m-%Y"_"%H_%M_%S .sql.gz 23 | + rm old backups: keep all date? + keep none date? 24 | sleep $$BACKUP_FREQUENCY 25 | done 26 | EOF' 27 | networks: 28 | - ... 29 | -------------------------------------------------------------------------------- /images/cache/Dockerfile: -------------------------------------------------------------------------------- 1 | # We build our own talkyard-cache image and push to talkyard-cache:$VERSION_TAG, and by 2 | # downloading and using that image, one automatically upgrades the Redis image, 3 | # whenever needed. (So won't need to configure Redis image version, separately, in Prod.) 4 | 5 | FROM redis:4.0.14-alpine 6 | 7 | -------------------------------------------------------------------------------- /images/certgen/Dockerfile: -------------------------------------------------------------------------------- 1 | # Later, I'll build a nginx server that listens on 82 to find out which HTTPS certs it 2 | # should generate, and then calls Let'sEncrypt. 3 | 4 | # Maybe Nginx with Lua HTTP handlers that calls out to Bash & certbot? 5 | 6 | FROM alpine:3.7 7 | 8 | # Stay alive forever. 9 | CMD ["tail", "-f", "/dev/null"] 10 | 11 | -------------------------------------------------------------------------------- /images/fakemail/README.md: -------------------------------------------------------------------------------- 1 | The self signed cert was generated something like so: 2 | 3 | ``` 4 | openssl genrsa -out server.key 2048 5 | openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 6 | ``` 7 | 8 | The cert, `fakemail-publ-test-self-signed.crt`, is placed here: 9 | `../app-dev/fakemail-publ-test-self-signed.crt`, so it can be copied 10 | into the app-dev Docker image, and added to the Java cert store [26UKWD2]. 11 | 12 | -------------------------------------------------------------------------------- /images/fakemail/fakemail-publ-test-self-signed.crt: -------------------------------------------------------------------------------- 1 | ../app/fakemail-publ-test-self-signed.crt -------------------------------------------------------------------------------- /images/fakemail/mailslurper-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "wwwAddress": "0.0.0.0", 3 | "wwwPort": 8026, 4 | "serviceAddress": "0.0.0.0", 5 | "servicePort": 8027, 6 | "smtpAddress": "0.0.0.0", 7 | "smtpPort": 8025, 8 | "dbEngine": "SQLite", 9 | "dbHost": "", 10 | "dbPort": 0, 11 | "dbDatabase": "/mailslurper.db", 12 | "dbUserName": "", 13 | "dbPassword": "", 14 | "maxWorkers": 10, 15 | "autoStartBrowser": false, 16 | "keyFile": "/smtp-server.key", 17 | "certFile": "/smtp-server.crt", 18 | "adminKeyFile": "", 19 | "adminCertFile": "" 20 | } 21 | 22 | -------------------------------------------------------------------------------- /images/fakeweb/app/deps.ts: -------------------------------------------------------------------------------- 1 | // This file isn't needed, yet? See ../docker/docker-entrypoint.sh . 2 | -------------------------------------------------------------------------------- /images/fakeweb/docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # So the deno user can cache Typescript files. 5 | chmod -R ugo+rw /deno-dir 6 | 7 | # Copied from: https://github.com/denoland/deno_docker/blob/main/_entry.sh 8 | # License: MIT, see ./LICENSE. 9 | 10 | # 11 | #if [ "$1" != "${1#-}" ]; then 12 | # # if the first argument is an option like `--help` or `-h` 13 | # exec deno "$@" 14 | #fi 15 | # 16 | #case "$1" in 17 | # bench | bundle | cache | compile | completions | coverage | doc | eval | fmt | help | info | install | lint | lsp | repl | run | task | test | types | uninstall | upgrade | vendor ) 18 | # # if the first argument is a known deno command 19 | # exec deno "$@";; 20 | #esac 21 | 22 | 23 | # Hmm, runs as root anyway: [deno_user] 24 | #exec su -c "deno cache deps.ts" deno 25 | exec su -c "deno cache main.ts" deno 26 | 27 | exec su -c "$@" deno -------------------------------------------------------------------------------- /images/keycloak/Dockerfile: -------------------------------------------------------------------------------- 1 | # SHOULD change to: jboss/keycloak ? [oidc_missing] 2 | # Repo: https://github.com/keycloak/keycloak-containers/tree/11.0.2 3 | FROM quay.io/keycloak/keycloak:11.0.0 4 | 5 | EXPOSE 8113 6 | EXPOSE 8553 7 | 8 | # Seems these doesn't work. 9 | ENV KEYCLOAK_HTTP_PORT=8113 10 | ENV KEYCLOAK_HTTPS_PORT=8553 11 | 12 | # Specifying -Djboss.http.port works though. 13 | CMD ["-b", "0.0.0.0", "-Djboss.http.port=8113"] 14 | 15 | # The entrypoint is: 16 | # ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ] 17 | # so that's why setting CMD to just "-b .. -D..." above, works 18 | # (that is, the entrypoint already calls the docker-entrypoint.sh script 19 | # — need not do from here). 20 | -------------------------------------------------------------------------------- /images/rdb/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:10.23-alpine 2 | 3 | RUN apk add --no-cache bash tree less vim curl net-tools 4 | 5 | COPY ./docker-entrypoint-initdb.d/ /docker-entrypoint-initdb.d/ 6 | COPY ./chown-logs-then-exec-entrypoint.sh / 7 | 8 | ENTRYPOINT ["/chown-logs-then-exec-entrypoint.sh"] 9 | CMD ["postgres"] 10 | 11 | -------------------------------------------------------------------------------- /images/rdb/chown-logs-then-exec-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Let user postgresql write log files. The log directory is mounted in docker-compose.yml 4 | # and is created as & owned by 'root'. — We chown here, because here we're root, but 5 | # /docker-entrypoint.sh called below will su-exec to 'postgres' (then, not allowed to chown). 6 | chown -R postgres /var/log/postgresql/ 7 | 8 | # Now continue with the "real" entrypoint, namely 9 | # https://github.com/docker-library/postgres/blob/master/10/alpine/docker-entrypoint.sh: 10 | exec /docker-entrypoint.sh $* 11 | 12 | -------------------------------------------------------------------------------- /images/search/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Don't chown on elasticsearch/ because then elasticsearch would get read-write access to its 4 | # own executable code — that'd be a security risk? 5 | chown -R elasticsearch /usr/share/elasticsearch/data 6 | chown -R elasticsearch /usr/share/elasticsearch/logs 7 | 8 | exec su -c "$*" elasticsearch 9 | 10 | -------------------------------------------------------------------------------- /images/web/README.adoc: -------------------------------------------------------------------------------- 1 | 2 | Ty uses LetsEncrypt and lua-resty-acme to generate HTTPS certs. 3 | 4 | The lua-resty-acme onfig is here: 5 | 6 | ./ty-lua/init-by-lua-file.lua 7 | 8 | And lua-resty-acme is here: 9 | 10 | ./openresty-pkgs/usr-local-openresty-site/lualib/resty/acme 11 | 12 | To upgrade Nginx / OpenResty, see: 13 | 14 | ./openresty/README.adoc 15 | 16 | 17 | == Writing Lua code 18 | 19 | Debugging: Use inspect(), like so: 20 | 21 | ``` 22 | local inspect = require 'inspect' 23 | log(ngx_INFO, "Something: ", inspect(a_map_for_example)) 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /images/web/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debiki/talkyard/f2c0a8e46b1065717a9a6645c72f905007816230/images/web/assets/.gitkeep -------------------------------------------------------------------------------- /images/web/fonts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debiki/talkyard/f2c0a8e46b1065717a9a6645c72f905007816230/images/web/fonts/.gitkeep -------------------------------------------------------------------------------- /images/web/gen-def-cert-letsencrypt-key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create a LetsEncrypt account key, so LetsEncrypt won't think this is a 4 | # new and different server all the time from the same IP — because that'd 5 | # make LetsEncryt rate limit it more. 6 | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 \ 7 | -out /etc/openresty/letsencrypt-account.key 8 | 9 | # Create a cert and key to use, before LetsEncrypt is done generating 10 | # a real cert. 11 | openssl req -newkey rsa:2048 -nodes -keyout /etc/openresty/default-cert.key \ 12 | -x509 -days 365 -out /etc/openresty/default-cert.pem 13 | -------------------------------------------------------------------------------- /images/web/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 |

404 Not Found
[TyE404NFNGX]

10 | 11 |

(This is Talkyard's Nginx server.)

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /images/web/html/503.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
 5 | 503 Service Unavailable [TyE503SU]
 6 | 
 7 | Error. Feel free to try again, in a little while.
 8 | 
 9 | You, or someone in your building or area, is likely sending too many computer network requests.
10 | And therefore you are being rate limited.
11 | 
12 | 
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /images/web/html/504.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
 5 | 504 Gateway Timeout [TyE504GWTO]
 6 | 
 7 | Error. The Talkyard app server takes too long.
 8 | 
 9 | You can try again, in a little while.
10 | 
11 | 
12 | 
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /images/web/html/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /-/ 3 | # Googlebot needs the CSS files to know that Talkyard is mobile friendly. 4 | # Probably good to let it access Javascript too? 5 | Allow: /-/assets/ 6 | # Googlebot needs to access these pages, to find the [noindex_tag] 7 | # tag and understand that these pages are 8 | # *not* to be indexed. However, relying on Disallow here in robots.txt (instead of 9 | # a meta-noindex tag) doesn't work — Googlebot might index them *anyway* if they're 10 | # linked to, from outside. 11 | Allow: /-/users/ 12 | Allow: /-/groups/ 13 | -------------------------------------------------------------------------------- /images/web/html/security.txt: -------------------------------------------------------------------------------- 1 | # For issues with this particular website, e.g. a page or category that ought to be private: 2 | # Contact the admins here, send them a private message. 3 | 4 | # For issues with the underlying discussion software powering this site, Talkyard: 5 | Contact: mailto:security@talkyard.io 6 | Contact: https://www.talkyard.io/contact 7 | -------------------------------------------------------------------------------- /images/web/html/session-iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /images/web/http-limits.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | limit_conn_zone $binary_remote_addr zone=conn_per_ip:20m; 4 | limit_conn_zone $server_name zone=conn_per_server:20m; 5 | 6 | limit_req_zone $binary_remote_addr zone=req_per_ip:20m rate=${ED_NGX_LIMIT_REQ_PER_IP}r/s; 7 | limit_req_zone $server_name zone=req_per_server:20m rate=${ED_NGX_LIMIT_REQ_PER_SERVER}r/s; 8 | 9 | 10 | -------------------------------------------------------------------------------- /images/web/http-redirect-to-https.conf: -------------------------------------------------------------------------------- 1 | # DELETE THIS FILE later 2 | # Redirect from HTTP to HTTPS. 3 | # Use temp redirect (302) not permanent (301) in case needs to revert to http for 4 | # a short while some day in the future. 5 | server { 6 | listen 80; 7 | listen [::]:80; 8 | server_name _; 9 | return 302 https://$http_host$request_uri; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /images/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ty-web-static-assets", 3 | "private": true, 4 | "dependencies": { 5 | "fontsource-open-sans": "^3.1.5" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /images/web/server-listen.conf: -------------------------------------------------------------------------------- 1 | # DELETE THIS FILE later 2 | listen 80; 3 | listen [::]:80; 4 | -------------------------------------------------------------------------------- /images/web/server-location-cdn.conf: -------------------------------------------------------------------------------- 1 | 2 | # Prevent people from accessing this URL directly on the origin server — only 3 | # allow access via the CDN. 4 | # See: https://www.keycdn.com/blog/restrict-cdn-traffic/ 5 | 6 | #if ($http_x_pull ~* "Your_secret_key") { 7 | # return 405; 8 | #} 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/web/ssl-cert-snakeoil.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICvDCCAaSgAwIBAgIJAMGL3y4HV3NfMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV 3 | BAMMC3VidW50dS1tYXRlMB4XDTE3MDEyNzEzNDQ0OFoXDTI3MDEyNTEzNDQ0OFow 4 | FjEUMBIGA1UEAwwLdWJ1bnR1LW1hdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw 5 | ggEKAoIBAQDZb6HV7PCzsx/2fjwn/qIhWVn4zNepha8JppGa0Mves/zxJfPf6FXA 6 | R/SjId3mE7II3r37XvO/vPmqTA1l+Uq9cRIWoDjpWWE52ijfxAX311akWed+/udC 7 | AHDflgngOf4yUeXolYCac6EnvikTb4EakwXsGbo0UCjA6N9cA37PIh+tDxvAm6c8 8 | NXb3DO/agRuI3jTxAUz8/IAZ4f2dU53KAkRzrpBbLI+Wxb4n/OteRZIXO6hElrdQ 9 | VwZ81TasSbs4xZ65LOO4vmFbzNmkn1HMH0ATfJjqSNeeOxhra32IGu30B/JcFrsr 10 | zgg4PCc37U1b8cLiznmqVANrGKB+iMdzAgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJ 11 | KoZIhvcNAQELBQADggEBAGuLECQB+9JZcbmsl61gn08pCqzM+XeZdqJMNkzPUPXK 12 | jQ+937fOtgpZRavQ/CliCwN6aVFbVKJSdDrMoB0tmooRTnXwaJ+W2p0Nm4AsnlKw 13 | ny7jkH7XGVxCYTqe8YP9IFZrA3SCNwRovXPwMS4ZwkLtnNiPT5NPSt0WTgzjdfmG 14 | OI7QtXbbauWPkNSdLL0tTkB4obxoH++KaZB3x2sI/YtYisrk/a1K4b8/Pubkm4XR 15 | 7EKM4jW4faFoLMO4n9qqhz8ZwHgR9XpbF8v9IYoxrYrVydjuhhb/Uu/RKtv3raH9 16 | PbYzVR3pidVlC9xXjvuwHrEwcdghA0D+rzM2y77RpDw= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /images/web/ty-lua/access-by-lua-file.lua: -------------------------------------------------------------------------------- 1 | local ty_lim_bw = require("lua-limit-bandwidth/access-phase") 2 | ty_lim_bw.slow_down_maybe() 3 | -------------------------------------------------------------------------------- /images/web/ty-lua/init-worker-by-lua-file.lua: -------------------------------------------------------------------------------- 1 | ngx.log(ngx.INFO, 'Running: init_worker_by_lua_block') 2 | 3 | -- The worker will share the 'local var_name' variables inited in init_by_lua_block{} 4 | -- above — read more here: 5 | -- https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker 6 | -- to learn about how data sharing in Nginx workers and Lua works. 7 | -- (Once inited, those local variables aren't changed — only read.) 8 | 9 | require("resty.acme.autossl").init_worker() 10 | -------------------------------------------------------------------------------- /images/web/ty-lua/log-by-lua-file.lua: -------------------------------------------------------------------------------- 1 | local ty_lim_bw = require("lua-limit-bandwidth/log-phase") 2 | ty_lim_bw.incr_used_bw() 3 | -------------------------------------------------------------------------------- /images/web/ty-lua/lua-limit-bandwidth/readme.md: -------------------------------------------------------------------------------- 1 | Rename to bad-bot-blocker? buster?, & try to stop all kinds of auto queries? 2 | 3 | 4 | http://lua-users.org/wiki/LuaModuleFunctionCritiqued 5 | http://www.lua.org/manual/5.1/manual.html#2.2 6 | 7 | ( http://www.londonlua.org/scripting_nginx_with_lua/slides.html#lua-subreq ) 8 | 9 | 10 | https://www.stavros.io/posts/writing-an-nginx-authentication-module-in-lua/ 11 | 12 | https://github.com/openresty/lua-resty-limit-traffic 13 | 14 | 15 | -------------------------------------------------------------------------------- /images/web/ty-lua/lua-limit-bandwidth/util.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | 4 | local function get_used_bw(dict, key) 5 | local bw, flags = dict:get(key) 6 | if bw == nil then 7 | bw = 0 8 | end 9 | return bw 10 | end 11 | 12 | 13 | local function set_used_bw(dict, per_what, key, bw) 14 | -- Ignore races. This needn't be totally exact. 15 | local expiration_seconds = 7 * 24 * 3600 16 | 17 | -- How avoid overwriting previous expiration time? By not specifying exptime?? 18 | local ok, err, forcible = dict:set(key, bw, expiration_seconds) 19 | 20 | if not ok then 21 | ngx.log(ngx.ERR, "Error adding " .. per_what .. " bandwidth to " .. key .. " [TyELUABWTH]") 22 | end 23 | 24 | if forcible then 25 | -- COULD log this at most once per day. 26 | ngx.log(ngx.WARN, "Per ip cache too small, old entry removed [TyWLUAIPSML]") 27 | end 28 | end 29 | 30 | 31 | M.get_used_bw = get_used_bw 32 | M.set_used_bw = set_used_bw 33 | 34 | return M 35 | 36 | -------------------------------------------------------------------------------- /images/web/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | fontsource-open-sans@^3.1.5: 6 | version "3.1.5" 7 | resolved "https://registry.yarnpkg.com/fontsource-open-sans/-/fontsource-open-sans-3.1.5.tgz#93d0040633d37215658d72565bd2a12d9a8fa854" 8 | integrity sha512-tculQB3D6GHLPcbY1+WduZJM3bitV7GptWuKBAU7emfagJ/D5jtMCMd4i4JOGEzk0n416fdo5vLC0zYHxBYxzg== 9 | -------------------------------------------------------------------------------- /modules/paseto-cmd/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /modules/paseto-cmd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "paseto-cmd" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | pasetors = { version = "0.6.7", features = ["v2"] } 10 | 11 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.10.7 2 | -------------------------------------------------------------------------------- /s/_tyd-completion-bash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generated like so: s/tyd --completion 3 | # Use like so: 4 | # alias tyd='s/tyd' 5 | # source s/_tyd-completion-bash.sh 6 | 7 | ### tyd completion - begin. generated by omelette.js ### 8 | if type compdef &>/dev/null; then 9 | _tyd_completion() { 10 | compadd -- `tyd --compzsh --compgen "${CURRENT}" "${words[CURRENT-1]}" "${BUFFER}"` 11 | } 12 | compdef _tyd_completion tyd 13 | elif type complete &>/dev/null; then 14 | _tyd_completion() { 15 | local cur prev nb_colon 16 | _get_comp_words_by_ref -n : cur prev 17 | nb_colon=$(grep -o ":" <<< "$COMP_LINE" | wc -l) 18 | 19 | COMPREPLY=( $(compgen -W '$(tyd --compbash --compgen "$((COMP_CWORD - (nb_colon * 2)))" "$prev" "${COMP_LINE}")' -- "$cur") ) 20 | 21 | __ltrim_colon_completions "$cur" 22 | } 23 | complete -F _tyd_completion tyd 24 | fi 25 | ### tyd completion - end ### 26 | 27 | -------------------------------------------------------------------------------- /s/d: -------------------------------------------------------------------------------- 1 | sudo docker-compose $@ 2 | -------------------------------------------------------------------------------- /s/d-debug-security-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # '--name test' is required, because the hostname in docker-compose (also 'test') 4 | # is ignored when using 'run'. 5 | 6 | set -x 7 | docker-compose \ 8 | run \ 9 | --rm \ 10 | -p 9229:9229 \ 11 | --name test \ 12 | test \ 13 | node --debug-brk --inspect=9229 node_modules/.bin/tape target/security-tests/**/*.js --host web 14 | 15 | -------------------------------------------------------------------------------- /s/d-down: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | s/d kill web app gulp ; s/d down 4 | -------------------------------------------------------------------------------- /s/d-gulp: -------------------------------------------------------------------------------- 1 | sudo docker-compose run --rm nodejs gulp $@ 2 | -------------------------------------------------------------------------------- /s/d-killcli: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | s/d kill web app 4 | exec s/d-cli 5 | 6 | -------------------------------------------------------------------------------- /s/d-killdown: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | s/d kill web app search gulp ; s/d down 4 | -------------------------------------------------------------------------------- /s/d-logs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo docker-compose logs --no-color $@ | s/impl/unjson.sh 4 | 5 | -------------------------------------------------------------------------------- /s/d-logsf0: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo docker-compose logs -f --tail 0 --no-color $@ | s/impl/unjson.sh 4 | 5 | -------------------------------------------------------------------------------- /s/d-psql: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # docker ps >> /dev/null 4 | # if [ $? -eq 1 ] ; then 5 | # echo "If the Docker daemon *is* running — then you can try with 'sudo'?" 6 | # exit 1 7 | # fi 8 | 9 | up_line=$(sudo docker-compose ps rdb | egrep '\') 10 | if [ -z "$up_line" ]; then 11 | echo "Error: The database Docker container 'rdb' is not running." 12 | echo "You can start it:" 13 | echo " docker-compose start rdb" 14 | exit 1 15 | fi 16 | 17 | if [ "$#" -ne 2 ]; then 18 | echo "Error: I didn't get exactly 2 parameters" 19 | echo "Usage: docker/psql.sh database username" 20 | exit 1 21 | fi 22 | 23 | sudo docker-compose exec rdb psql $1 $2 24 | -------------------------------------------------------------------------------- /s/d-restart: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # CLEAN_UP REMOVE do `s/tyd restart` instead 4 | # Restart gulp too, because not until restarted will it notice if client/third-party/ 5 | # Javascript has been modified. 6 | # And restart Nginx too (the 'web' container), so it'll notice if config changed. 7 | s/d kill web app gulp ; s/d start web app gulp ; s/d-logs -f --tail 0 8 | -------------------------------------------------------------------------------- /s/d-restart-web-app: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | s/d kill web app ; s/d start web app ; s/d-logs -f --tail 0 4 | -------------------------------------------------------------------------------- /s/d-run-security-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # '--name test' is required, because the hostname in docker-compose (also 'test') 4 | # is ignored when using 'run'. 5 | 6 | set -x 7 | docker-compose \ 8 | run \ 9 | --rm \ 10 | --name test \ 11 | test \ 12 | node_modules/.bin/tape target/security-tests/**/*.js --host web 13 | 14 | -------------------------------------------------------------------------------- /s/d-start-ed-prod-one-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_containers="VERSION_TAG=latest docker-compose -p edt -f modules/ed-prod-one-test/docker-compose.yml -f modules/ed-prod-one-test/debug.yml -f modules/ed-prod-one-test-override.yml" 4 | 5 | $test_containers "$@" 6 | 7 | # vim: et ts=2 sw=2 tw=0 fo=r 8 | -------------------------------------------------------------------------------- /s/d-stats: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo docker stats `sudo docker ps --format '{{.Names}}'` 3 | 4 | -------------------------------------------------------------------------------- /s/d-up-d-logsf0: -------------------------------------------------------------------------------- 1 | s/d up -d ; s/d-logsf0 2 | -------------------------------------------------------------------------------- /s/delete-container-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | echo '' > `docker inspect --format='{{.LogPath}}' $1` 6 | 7 | -------------------------------------------------------------------------------- /s/diff-ty-dirs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # diff -q -r ../d7/ ./ | egrep -v 'vendors/jars|/volumes|/node_modules|/target|report\..*\.json|/logs|/gatsby-starter|/\.git|/\.idea/|/dist|/\.bloop|/\.metals' 4 | 5 | -------------------------------------------------------------------------------- /s/git-delete-branch-here-and-origin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on error. 4 | set -e 5 | 6 | branch_name="$1" 7 | 8 | git branch -D "$branch_name" 9 | git push origin --delete "$branch_name" 10 | 11 | 12 | # Delete many with the same name part: 13 | # for b in $(git branch -a | grep dependabot | sed s+remotes/origin/++ ) ; do echo git push origin --delete $b ; done 14 | 15 | # Delete all from a specific repository, say, tytest, locally only: (flags -d -r deletes the remote branch, locally only) 16 | # 17 | # for b in $(git branch -a | grep tytest | sed 's#remotes/##') ; do git branch -d -r $b ; done 18 | 19 | 20 | -------------------------------------------------------------------------------- /s/git-delete-tag-here-and-origin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on error. 4 | set -e 5 | 6 | tag_name="$1" 7 | 8 | git tag --delete "$tag_name" 9 | git push origin ":refs/tags/$tag_name" 10 | 11 | 12 | -------------------------------------------------------------------------------- /s/git-diff-patches.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is for comparing the changes introduced by two commits on 4 | # different branches, so you'll know if they do the same things. 5 | # NOT for comparing everything in those two different revisions 6 | # — just the changes in those particular commits / patches. 7 | 8 | 9 | # <(... $1) creates a file descriptor with the contents of commit $1 10 | # — just the changes in that commit, i.e. that patch — and another file 11 | # descriptor with $2, and compares the difference with Meld, a diff viewer. 12 | # 13 | meld <(git show "$1") <(git show "$2") 14 | 15 | -------------------------------------------------------------------------------- /s/git-range-stats.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: s/git-range-stats.sh FROM_EXCL TO_INCL 4 | 5 | # Shows files changed, and num lines, for each revision from but not including 6 | # FROM_EXCL up to and including TO_INCL. 7 | 8 | echo 9 | echo "Here's stats for all commits" 10 | echo "from excl $(git log --oneline -n1 $1)" 11 | echo "up to incl $(git log --oneline -n1 $2)" 12 | echo 13 | 14 | for r in $(git rev-list $1..$2); do 15 | echo 16 | git log --oneline -n1 $r 17 | git diff --stat $r^ $r 18 | done 19 | 20 | 21 | -------------------------------------------------------------------------------- /s/git-show-changed-files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shows which files changed, in each commit from-commit to-commit. 4 | # But not what changed inside those files. 5 | # — Nice when cleaning up a private branch, so can place related commits 6 | # next to each other, and squash. 7 | 8 | 9 | if [ "$#" -ne "2" ]; then 10 | echo "Usage: $0 from-commit to-commit" 11 | exit 1 12 | fi 13 | 14 | for commit_hash in $(git log --oneline $1...$2 | awk '{ print $1 }') ; do 15 | git show --stat --oneline $commit_hash 16 | done 17 | 18 | -------------------------------------------------------------------------------- /s/git-show-tags-by-date.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Prints tags, rows like: 4 | # 5 | # 2020-08-23 09:46:31 +0200 2c26eb4 (tag: cr-done-4765762) 6 | # 7 | 8 | git log --date-order --tags --graph --simplify-by-decoration --pretty=format:'%ai %h %d' 9 | 10 | 11 | -------------------------------------------------------------------------------- /s/kill-wdio: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Sometimes wdio becomes half dead, with ports still open. Then: 3 | # awk $2 = the 2nd column = the process id. 4 | 5 | wdio_pids="$(ps aux | grep wdio | egrep -v 'grep wdio|kill-wdio' | awk '{ print $2 }')" 6 | 7 | if [ -n "$wdio_pids" ]; then 8 | echo "Killing wdio:" 9 | echo kill $wdio_pids 10 | kill $wdio_pids 11 | else 12 | echo "No wdio to kill." 13 | fi 14 | 15 | -------------------------------------------------------------------------------- /s/old/run-test-suite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$play" ]; then 4 | play=scripts/play-2.2.3 5 | fi 6 | 7 | timeout_play="$play" 8 | if [ "$1" = "--timeout" ]; then 9 | shift 10 | timeout_play="scripts/timeout.sh -t $1 -d 10 $play" 11 | shift 12 | fi 13 | 14 | $timeout_play "$@" 15 | if [ $? -ne 0 ]; then 16 | echo "$@" >> target/tests-failed 17 | fi 18 | -------------------------------------------------------------------------------- /s/selenium-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // These docs: 3 | // https://www.npmjs.com/package/selenium-standalone#application-programming-interface-api 4 | // says you'll find the most recent Selenium version number here: 5 | // https://selenium-release.storage.googleapis.com/index.html 6 | version: '3.9.1', // seems 4.0 is alpha, Feb 2020? 7 | drivers: { 8 | chrome: { 9 | // Should match the Chrome version you use. 10 | // Find new versions here: 11 | // https://chromedriver.storage.googleapis.com/index.html 12 | version: '80.0.3987.16', 13 | //version: '81.0.4044.69', 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /s/selenium-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # To download a specific Chromedriver version: (e.g. v 2.38) 4 | # s/selenium-install --drivers.chrome.version=2.38 5 | 6 | node_modules/selenium-standalone/bin/selenium-standalone install --config=s/selenium-config.js $@ 7 | 8 | -------------------------------------------------------------------------------- /s/selenium-start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Prefix with: DEBUG=selenium-standalone:* to tell Selenium to debug log more. 4 | # And optionally, append: --javaArgs="-Dwebdriver.chrome.logfile=chromedriver.log 5 | # to get a little bit Chromedriver log messages in ./chromedriver.log. 6 | # 7 | # However, the most interesting log messages, are when you invoke wdio, 8 | # that is, not this script, but s/wdio. Append --logLevel=verbose, 9 | # for example: 10 | # s/wdio target/e2e/wdio.conf.js --only some-test --logLevel=verbose 11 | 12 | node_modules/selenium-standalone/bin/selenium-standalone start --config=s/selenium-config.js $@ 13 | 14 | -------------------------------------------------------------------------------- /s/selenium-start-invisible: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Prefix the selenium-standalone command with: DEBUG=selenium-standalone:* 4 | # to tell Selenium to debug log more. 5 | 6 | xvfb-run -s '-screen 0 1280x1024x8' node_modules/selenium-standalone/bin/selenium-standalone start --config=s/selenium-config.js $@ 7 | 8 | -------------------------------------------------------------------------------- /s/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "outDir": "../target/s-tyd", 5 | "baseUrl": ".", 6 | "paths": { 7 | "*": [ "./*" ], 8 | "src/*": ["./*"] 9 | }, 10 | "types": [ 11 | "node", 12 | //"@wdio/cli", 13 | "ansi-colors", 14 | "assert", 15 | "core-js", 16 | "lodash"] 17 | }, 18 | "include": [ 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /s/tyd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | if [ ! -d ~/.nix-profile ]; then 4 | echo 5 | echo "First install Nix — see docs/starting-talkyard.md. Bye for now." 6 | echo 7 | exit 1 8 | fi 9 | 10 | if [ -z "$IN_NIX_SHELL" ]; then 11 | echo 12 | echo "First start Nix shell:" 13 | echo 14 | echo " nix-shell" 15 | echo 16 | echo "Then try this script again (inside the Nix shell). Bye." 17 | echo 18 | exit 1 19 | fi 20 | 21 | if [ -z "$(which yarn)" ]; then 22 | echo 23 | echo "Wrong Nix-shell? I don't see any 'yarn' executable, but I need" 24 | echo "Yarn sometimes, to transpile Typescript. Error. Bye." 25 | echo 26 | exit 1 27 | fi 28 | 29 | # Download any missing Git submodules. 30 | make git-subm-init-upd 31 | 32 | # Download ts-node — s/tyd.js (below) calls tyd.ts (Typescript). 33 | make node_modules 34 | 35 | s/tyd.js "$@" 36 | -------------------------------------------------------------------------------- /s/tyd.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('ts-node').register({ transpileOnly: true }); 4 | 5 | // Include the '.ts' suffix, otherwise apparently any '.js' file with the same 6 | // name (excl suffix) gets loaded. 7 | exports.config = require('./tyd.ts'); 8 | 9 | -------------------------------------------------------------------------------- /s/wdio: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd tests/e2e/ 4 | 5 | 6 | # [E2EHTTPS] 7 | # This is old Webdriver 6, since we're in tests/e2e/ not e2e-wdio7/. [wdio_6_to_7] 8 | cmd="node_modules/.bin/wdio wdio.conf.js $@" 9 | echo 10 | echo "NODE_TLS_REJECT_UNAUTHORIZED=0 $cmd" 11 | echo 12 | NODE_TLS_REJECT_UNAUTHORIZED=0 $cmd 13 | exit_code=$? 14 | 15 | if [ $exit_code -ne 0 ]; then 16 | echo 17 | echo 18 | echo "Error. E2E test failed, exit code: $exit_code" 19 | echo 20 | echo "Was started like so:" 21 | echo " NODE_TLS_REJECT_UNAUTHORIZED=0 $cmd" 22 | fi 23 | 24 | echo 25 | exit $exit_code 26 | -------------------------------------------------------------------------------- /s/wdio-7: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd tests/e2e-wdio7/ 4 | 5 | 6 | # [E2EHTTPS] 7 | # This is new Webdriver 7, since we're in tests/e2e-wdio7/. [wdio_6_to_7] 8 | cmd="node_modules/.bin/wdio wdio.conf.ts $@" 9 | echo 10 | echo "NODE_TLS_REJECT_UNAUTHORIZED=0 $cmd" 11 | echo 12 | NODE_TLS_REJECT_UNAUTHORIZED=0 $cmd 13 | exit_code=$? 14 | 15 | if [ $exit_code -ne 0 ]; then 16 | echo 17 | echo 18 | echo "Error. Webdriverio 7 E2E test failed, exit code: $exit_code" 19 | echo 20 | echo "Was started like so:" 21 | echo " NODE_TLS_REJECT_UNAUTHORIZED=0 $cmd" 22 | fi 23 | 24 | echo 25 | exit $exit_code 26 | -------------------------------------------------------------------------------- /s/wdio-debug-9101: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | # Old; 5 | # If we --inspect on 9100, wdio apparently forks a child process listening on 9100 + 1 = 9101. 6 | # To which you can connect like so: `node debug 127.0.0.1:9101` 7 | 8 | 9 | # The default inspect ip:port is: 127.0.0.1:9229 10 | 11 | cd tests/e2e/ 12 | 13 | # [E2EHTTPS] 14 | NODE_TLS_REJECT_UNAUTHORIZED=0 node --inspect ../../node_modules/.bin/wdio wdio.conf.js $@ 15 | exit_code=$? 16 | 17 | if [ $exit_code -ne 0 ]; then 18 | echo 19 | echo "Error. E2E test failed, exit code: $exit_code" 20 | fi 21 | 22 | echo 23 | exit $exit_code 24 | -------------------------------------------------------------------------------- /s/yarn: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Gah! 'yarn' and 'yarn install' nowadays (or did it always?) deletes ./node_modules/, 4 | # see: [yarn_deletes_mods_dir] in s/tyd.ts. 5 | # 6 | sudo s/d run --rm nodejs yarn 7 | 8 | -------------------------------------------------------------------------------- /tests/api/jest-concise-console.ts: -------------------------------------------------------------------------------- 1 | // Jest wraps log messages in e.g.: 2 | // console.log 3 | // ... the actual message ... 4 | // 5 | // at SomeClass.someLogMsgFn (../some/file.ts:123:45) 6 | // 7 | // which makes the log messages very verbose. 8 | // 9 | // This custom console doesn't do that; it only logs the actual messages. 10 | // 11 | // See: "Remove logging the origin line in Jest" 12 | // https://stackoverflow.com/a/57443150 13 | 14 | 15 | import { CustomConsole, LogType, LogMessage } from '@jest/console'; 16 | 17 | function conciseFormatter(type: LogType, message: LogMessage): St { 18 | // "LM" stands for "log message". 19 | return message.split('\n').map(line => 20 | ' LM ' + line).join('\n'); 21 | } 22 | 23 | global.console = new CustomConsole(process.stdout, process.stderr, conciseFormatter); -------------------------------------------------------------------------------- /tests/app/debiki/dao/TestSiteAndDao.scala: -------------------------------------------------------------------------------- 1 | package debiki.dao 2 | 3 | import com.debiki.core._ 4 | import com.debiki.core.Prelude._ 5 | 6 | 7 | class TestSiteAndDao( 8 | val siteId: SiteId, 9 | val daoAppSuite: DaoAppSuite) { 10 | 11 | def globals: debiki.Globals = daoAppSuite.globals 12 | 13 | var site: Site = _ 14 | var daoStale: Bo = false 15 | private var curDaoMaybeStale: SiteDao = _ 16 | 17 | 18 | def id: SiteId = site.id 19 | 20 | def dao: SiteDao = { 21 | if (curDaoMaybeStale eq null) { 22 | if (siteId == Site.FirstSiteId) { 23 | site = globals.systemDao.getOrCreateFirstSite() 24 | curDaoMaybeStale = globals.siteDao(siteId) 25 | } 26 | else { 27 | val (newSite, newDao) = daoAppSuite.createSite(s"site-$siteId") 28 | site = newSite 29 | curDaoMaybeStale = newDao 30 | } 31 | } 32 | else if (daoStale) { 33 | curDaoMaybeStale = globals.siteDao(siteId) 34 | daoStale = false 35 | } 36 | 37 | curDaoMaybeStale 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/.gitignore: -------------------------------------------------------------------------------- 1 | embeds-my-ip-and-topic.html 2 | ignored/ 3 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-localhost-topic-id-1001-jquery-2.1-pre-loaded.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 | 9 | 10 |

Text text text

11 |

Text text text

12 |

Text text text

13 |

Text text text

14 |

15 | 16 |
17 | 18 |

Powered by ...

19 |
20 | 21 |

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 |

Text text text

26 |

27 |

Text text text

28 |

Text text text

29 |

Text text text

30 |

Text text text

31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-localhost-topic-id-1001-modernizr-2.7-pre-loaded.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 | 9 | 10 |

Text text text

11 |

Text text text

12 |

Text text text

13 |

Text text text

14 |

15 | 16 |
17 | 18 |

Powered by ...

19 |
20 | 21 |

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 |

Text text text

26 |

27 |

Text text text

28 |

Text text text

29 |

Text text text

30 |

Text text text

31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-localhost-topic-id-1001.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 | 9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

Text text text

13 |

14 | 15 |
16 | 17 |

Powered by ...

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-localhost-topic-id-1002.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 | 9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

Text text text

13 |

14 | 15 |
16 | 17 |

Powered by ...

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-site-1-topic-id-empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 |

Text text text

9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

13 | 14 | 15 |
16 | 17 |

Comments powered by Debiki.

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-site-11-topic-id-empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 |

Text text text

9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

13 | 14 | 15 |
16 | 17 |

Comments powered by Debiki.

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-site-12-topic-id-empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 |

Text text text

9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

13 | 14 | 15 |
16 | 17 |

Comments powered by Debiki.

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-site-13-topic-id-empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 |

Text text text

9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

13 | 14 | 15 |
16 | 17 |

Comments powered by Debiki.

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-site-14-topic-id-empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 |

Text text text

9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

13 | 14 | 15 |
16 | 17 |

Comments powered by Debiki.

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/embeds-site-15-topic-id-empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Static Page Example 5 | 6 | 7 | 8 |

Text text text

9 |

Text text text

10 |

Text text text

11 |

Text text text

12 |

13 | 14 | 15 |
16 | 17 |

Comments powered by Debiki.

18 |
19 | 20 |

21 |

Text text text

22 |

Text text text

23 |

Text text text

24 |

Text text text

25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/readme.txt: -------------------------------------------------------------------------------- 1 | Find here static HTML pages that use Debiki Embedded Comments. That is, they 2 | contain iframe(s) that show embedded comments. 3 | 4 | The iframes assume that a Play Framework listens on port 19001, because 5 | that's the port on which Play's test server listens. 6 | 7 | *** 8 | 9 | To serve the pages in this folder over HTTP on port 8080, do this: 10 | 11 | Install Node.js 12 | $ sudo npm install http-server -g 13 | $ cd to-this-directory 14 | $ http-server 15 | 16 | Also add entries like 17 | 127.0.0.1 mycomputer 18 | 127.0.0.1 site-10.localhost 19 | 127.0.0.1 site-11.localhost 20 | 127.0.0.1 site-12.localhost 21 | 127.0.0.1 site-13.localhost 22 | 127.0.0.1 site-14.localhost 23 | 127.0.0.1 site-15.localhost 24 | ... 25 | to your hosts file (/etc/hosts). Then the embedding pages will be served from 26 | one domain, and the embedded iframes from another, which tests that Debiki 27 | doesn't have any cross domain issues. 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-2.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-3.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-4.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-5.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-6.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-7.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-8.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/app/resources/embedding-pages/static-page-no-id-9.html: -------------------------------------------------------------------------------- 1 | static-page-no-id.html -------------------------------------------------------------------------------- /tests/e2e-wdio7/modules: -------------------------------------------------------------------------------- 1 | ../../modules/ -------------------------------------------------------------------------------- /tests/e2e-wdio7/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ty-e2e-wdio7", 3 | "description": "Talkyard's e2e tests in Webdriverio 7, slowly upgrading from v6", 4 | "version": "0.0.1", 5 | "author": "Kaj Magnus Lindberg", 6 | "license": "AGPL-3.0-or-later", 7 | "devDependencies": { 8 | "@wdio/cli": "^7.20.5", 9 | "@wdio/devtools-service": "^7.20.5", 10 | "@wdio/firefox-profile-service": "^7.20.3", 11 | "@wdio/local-runner": "^7.20.5", 12 | "@wdio/mocha-framework": "^7.20.3", 13 | "@wdio/reporter": "^7.20.3", 14 | "@wdio/spec-reporter": "^7.20.3", 15 | "@wdio/types": "^7.20.3", 16 | "axios": "^0.26.1", 17 | "chromedriver": "^135.0.2", 18 | "paseto.js": "^0.1.7", 19 | "ts-node": "^10.9.1", 20 | "wdio-chromedriver-service": "^7.3.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/pageobjects/page.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * main page object containing all methods, selectors and functionality 3 | * that is shared across all page objects 4 | */ 5 | export default class Page { 6 | /** 7 | * Opens a sub page of the page 8 | * @param path path of the sub page (e.g. /path/to/page.html) 9 | */ 10 | open (path: string) { 11 | return browser.url(`https://the-internet.herokuapp.com/${path}`) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/pageobjects/secure.page.ts: -------------------------------------------------------------------------------- 1 | import Page from './page'; 2 | 3 | /** 4 | * sub page containing specific selectors and methods for a specific page 5 | */ 6 | class SecurePage extends Page { 7 | /** 8 | * define selectors using getter methods 9 | */ 10 | get flashAlert () { return $('#flash') } 11 | } 12 | 13 | export default new SecurePage(); 14 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/d.oidc-azure-login-required.2br.extidp.e2e.ts: -------------------------------------------------------------------------------- 1 | import { addOidcAzureTestSteps } from './d.oidc-azure-impl'; 2 | 3 | describe(`oidc-azure-login-required.2br.extidp TyTE2E60RTE24`, () => { 4 | addOidcAzureTestSteps({ loginRequired: true }); 5 | }); 6 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/d.oidc-azure-pub-site.2br.extidp.e2e.ts: -------------------------------------------------------------------------------- 1 | import { addOidcAzureTestSteps } from './d.oidc-azure-impl'; 2 | 3 | describe(`oidc-azure-pub-site.2br.extidp TyTE2EOIDCAZ02`, () => { 4 | addOidcAzureTestSteps({ loginRequired: false }); 5 | }); 6 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/embcom.expimpjson.imp-to-existing-site.2br.e2e-UNIMPL.ts: -------------------------------------------------------------------------------- 1 | import constructEmbCommentsImportTest from './embcom.expimpjson.import-tests-impl'; 2 | 3 | 4 | constructEmbCommentsImportTest("emb-cmts-patch-existing-site TyT7WKGKS269", { 5 | importToExistingEmptyEmbCommentsSiteViaApi: true }); 6 | 7 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/embcom.expimpjson.imp-to-new-site.2br.e2e.ts: -------------------------------------------------------------------------------- 1 | import constructEmbCommentsImportTest from './embcom.expimpjson.import-tests-impl'; 2 | 3 | 4 | constructEmbCommentsImportTest( 5 | `embedded-comments-import-json-create-new-site.2browsers.test.ts TyT4SLCD24S`, { 6 | importToNewSite: true }); 7 | 8 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/embcom.expimpjson.restore-overwrite-site-new-domain.2br.e2e.ts: -------------------------------------------------------------------------------- 1 | import constructEmbCommentsImportTest from './embcom.expimpjson.import-tests-impl'; 2 | 3 | 4 | constructEmbCommentsImportTest( 5 | `embedded-comments-restore-overwrite-site-new-domain.2browsers.test.ts TyT603KNF62`, { 6 | restoreOverwriteSiteViaAdminButtonToNewDomain: true }); 7 | 8 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/embcom.expimpjson.restore-overwrite-site-same-domain.2br.e2e.ts: -------------------------------------------------------------------------------- 1 | import constructEmbCommentsImportTest from './embcom.expimpjson.import-tests-impl'; 2 | 3 | 4 | constructEmbCommentsImportTest("emb-cmts-restore-overwr-same-domain TyT5WKTJL025", { 5 | restoreOverwriteSiteViaAdminButtonToSameDomain: true }); 6 | 7 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/embcom.manyframes.js-api.2br.ec.e2e.ts: -------------------------------------------------------------------------------- 1 | 2 | import { addEmbComManyFramesTests } from './embcom.manyframes.js-api.impl'; 3 | 4 | describe(`embcom.manyframes.js-api.2br.ec TyTEMANYEMBDISAPI`, () => { 5 | addEmbComManyFramesTests({ 6 | usingSingleSignOn: false, 7 | localHostname: 'comments-for-e2e-test-manyapi', 8 | embeddingOrigin: 'http://e2e-test-manyapi.localhost:8080', 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/embcom.manyframes.js-api.sso.2br.ec.e2e.ts: -------------------------------------------------------------------------------- 1 | 2 | import { addEmbComManyFramesTests } from './embcom.manyframes.js-api.impl'; 3 | 4 | 5 | describe(`embcom.manyframes.js-api.sso.2br.ec TyTEMANYEMBDISAPISSO`, () => { 6 | addEmbComManyFramesTests({ 7 | usingSingleSignOn: true, 8 | localHostname: 'comments-for-e2e-test-manyapisso', 9 | embeddingOrigin: 'http://e2e-test-manyapisso.localhost:8080', 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/specs/load-test-100-pages-60-users.2br.e2e.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { addTestsForConstructingLoadTestSiteAndLoggingIn } from './load-test-site-builder'; 4 | 5 | 6 | describe(`some-e2e-test TyTE2E1234ABC`, () => { 7 | 8 | addTestsForConstructingLoadTestSiteAndLoggingIn({ 9 | siteName: "Load Test Site 100 Pages 60 Users", 10 | numPages: 100, 11 | numUsers: 60, 12 | }); 13 | 14 | }); 15 | 16 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/target: -------------------------------------------------------------------------------- 1 | ../../target -------------------------------------------------------------------------------- /tests/e2e-wdio7/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "types": [ 5 | "node", 6 | "webdriverio/sync", 7 | "@wdio/mocha-framework", 8 | "expect-webdriverio", 9 | "@wdio/devtools-service", 10 | "@wdio/firefox-profile-service"] 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/e2e-wdio7/utils/settings.ts: -------------------------------------------------------------------------------- 1 | settings-exp-def.ts -------------------------------------------------------------------------------- /tests/e2e-wdio7/utils/ty-e2e-post.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import * as _ from 'lodash'; 5 | import * as utils from '../utils/utils'; 6 | import c from '../test-constants'; 7 | 8 | type WElm = WebdriverIO.Element; 9 | 10 | 11 | export function postElem(elem: WElm): PostElem { 12 | return new PostElem(elem); 13 | } 14 | 15 | 16 | export class PostElem { 17 | 18 | #postElem: WElm; 19 | 20 | constructor(elem: WElm) { 21 | this.#postElem = elem; 22 | } 23 | 24 | async getText(): Pr { 25 | const bodyElm = await this.#postElem.$('.dw-p-bd'); 26 | return await bodyElm.getText(); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Kaj Magnus Lindberg", 3 | "name": "ty-e2e-wdio6", 4 | "description": "Talkyard's e2e tests in Webdriverio 6", 5 | "license": "AGPL-3.0-or-later", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@wdio/cli": "=6.10.10", 9 | "@wdio/devtools-service": "=6.10.10", 10 | "@wdio/dot-reporter": "=6.10.6", 11 | "@wdio/firefox-profile-service": "=6.10.6", 12 | "@wdio/local-runner": "=6.10.10", 13 | "@wdio/mocha-framework": "=6.10.10", 14 | "@wdio/selenium-standalone-service": "=6.10.10", 15 | "@wdio/spec-reporter": "=6.10.6", 16 | "@wdio/static-server-service": "=6.10.10", 17 | "@wdio/sync": "=6.10.10", 18 | "chromedriver": "^93.0.1", 19 | "fibers": "v4.x doesn't work with Nodejs 14, which Ty uses — v12 is EOL (and odd versions like v13 are unstable).", 20 | "fibers": "5.0.1", 21 | "paseto.js": "^0.1.7", 22 | "wdio-chromedriver-service": "=6.0.4" 23 | }, 24 | "resolutions": { 25 | "fibers": "Webdriver.io 6 tries to use Fibers 4.x.", 26 | "fibers": "5.0.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/readme.md: -------------------------------------------------------------------------------- 1 | In this directory: End-to-End tests using Webdriver.io. 2 | 3 | See <../../../docs/e2e-tests.md> 4 | 5 | -------------------------------------------------------------------------------- /tests/e2e/specs/api-private-chat-two-pps-list-use-usernames.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import addApiChatTestSteps from './api-private-chat-two-pps-impl.test'; 4 | 5 | 6 | describe("api-private-chat-two-pps-list-use-usernames TYT6924VBNF962", () => { 7 | 8 | addApiChatTestSteps({ lookupAndUseUsernames: true }); 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /tests/e2e/specs/api-private-chat-two-pps-sso-extid.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import addApiChatTestSteps from './api-private-chat-two-pps-impl.test'; 4 | 5 | 6 | describe("api-private-chat-two-pps-sso-extid TyT603WKVJW336", () => { 7 | 8 | addApiChatTestSteps({ useExtIdAndSsoId: true }); 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /tests/e2e/specs/embedded-comments-navigation-as-guest.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import navAsSomeoneTests = require('./navigation-as-impl'); 4 | 5 | navAsSomeoneTests(() => { // TyT2P067WKT2 6 | return { 7 | fullName: 'Greta Gäst', 8 | isGuest: true, 9 | }; 10 | }); 11 | 12 | -------------------------------------------------------------------------------- /tests/e2e/specs/navigation-as-admin.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import navAsSomeoneTests = require('./navigation-as-impl'); 4 | 5 | navAsSomeoneTests(() => { // TyT7WAAR2J4 6 | return { 7 | member: 'alice', 8 | memberIsAdmin: true, 9 | }; 10 | }); 11 | 12 | -------------------------------------------------------------------------------- /tests/e2e/specs/navigation-as-member.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import navAsSomeoneTests = require('./navigation-as-impl'); 4 | 5 | navAsSomeoneTests(() => { // TyT2ABKR593 6 | return { 7 | member: 'michael', 8 | }; 9 | }); 10 | 11 | -------------------------------------------------------------------------------- /tests/e2e/specs/navigation-as-stranger.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import navAsSomeoneTests = require('./navigation-as-impl'); 4 | 5 | navAsSomeoneTests(() => { // TyT5WKAB024 6 | return { 7 | member: null, 8 | }; 9 | }); 10 | 11 | -------------------------------------------------------------------------------- /tests/e2e/specs/sso-approval-required.UNIMPL.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | import constructSsoLoginTest from './sso-login-member-impl.2browsers.test'; 2 | 3 | 4 | constructSsoLoginTest("sso-approval-required TyT502RKDJH3", { 5 | loginRequired: false, approvalRequired: true }); 6 | -------------------------------------------------------------------------------- /tests/e2e/specs/sso-login-and-approval-required.UNIMPL.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | import constructSsoLoginTest from './sso-login-member-impl.2browsers.test'; 2 | 3 | 4 | constructSsoLoginTest("sso-login-and-approval-required TyT503KRTTR204BR", { 5 | loginRequired: true, approvalRequired: true }); 6 | -------------------------------------------------------------------------------- /tests/e2e/specs/sso-login-member.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | import constructSsoLoginTest from './sso-login-member-impl.2browsers.test'; 2 | 3 | 4 | constructSsoLoginTest("sso-login-member TyT5HNATS20P", { 5 | loginRequired: false, approvalRequired: false }); 6 | 7 | -------------------------------------------------------------------------------- /tests/e2e/specs/sso-login-required-w-logout-url.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | import constructSsoLoginTest from './sso-login-member-impl.2browsers.test'; 2 | 3 | 4 | constructSsoLoginTest("sso-login-required-w-logout-url.2browsers TyTE2ESSOLGOURL", { 5 | loginRequired: true, 6 | // This server and page don't exist; the browser will show an error. Fine. 7 | ssoLoginRequiredLogoutUrl: 'http://localhost:8080/after-logout-page.html', 8 | approvalRequired: false }); 9 | -------------------------------------------------------------------------------- /tests/e2e/specs/sso-login-required.2browsers.test.ts: -------------------------------------------------------------------------------- 1 | import constructSsoLoginTest from './sso-login-member-impl.2browsers.test'; 2 | 3 | 4 | constructSsoLoginTest("sso-login-required TyT7KSD20RG42", { 5 | loginRequired: true, approvalRequired: false }); 6 | -------------------------------------------------------------------------------- /tests/e2e/specs/sso.logout-url.2br.test.ts: -------------------------------------------------------------------------------- 1 | import * as u from '../utils/utils'; 2 | import constructSsoLoginTest from './sso-login-member-impl.2browsers.test'; 3 | 4 | 5 | constructSsoLoginTest(`sso.logout-url.2br TyTE2ESSOLGOURL2`, { 6 | loginRequired: false, 7 | ssoLogoutUrl: `http://localhost:8080/${u.ssoLogoutRedirPageSlug}`, 8 | approvalRequired: false }); 9 | -------------------------------------------------------------------------------- /tests/e2e/target: -------------------------------------------------------------------------------- 1 | ../../target/ -------------------------------------------------------------------------------- /tests/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // So can: 'import .. from', for something that was CommonJS-style 'export = ...'. 4 | //"allowSyntheticDefaultImports": true, 5 | //"esModuleInterop": true, 6 | "target": "ES2017", 7 | "outDir": "../../target/e2e-ts-node", 8 | "baseUrl": ".", 9 | "paths": { 10 | "*": [ "./*" ], 11 | "src/*": ["./*"] 12 | }, 13 | "types": [ 14 | "node", 15 | "@wdio/sync", 16 | "@wdio/reporter", 17 | "ansi-colors", 18 | "assert", 19 | "core-js", 20 | "lodash"] 21 | }, 22 | "include": [ 23 | "../../client/app-slim/constants.ts", 24 | "../../client/app-slim/model.ts", 25 | "./**/*.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/utils/settings.ts: -------------------------------------------------------------------------------- 1 | import settings from './settings-exp-def'; 2 | 3 | // Old export. Remove, use settings-exp-def.ts instead and 4 | // replace this file with that file. 5 | export = settings; 6 | -------------------------------------------------------------------------------- /tests/emb-forum-irame-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedded comments E2E test 5 | 6 | 7 |

Embedded comments E2E test page aaa, discussion-id="123abc".
8 | Ok to delete. The comments: ("long ago" generated by the admin js bundle [2JKWTQ0]) 9 |

10 | 11 | 16 | 17 | 18 | 19 |

/End of page.

20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/test-media: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/test-media -------------------------------------------------------------------------------- /to-talkyard/.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.swp 10 | 11 | pids 12 | logs 13 | results 14 | tmp 15 | 16 | # Build 17 | public/css/main.css 18 | 19 | # Coverage reports 20 | coverage 21 | 22 | # API keys and secrets 23 | .env 24 | 25 | # Dependency directory 26 | node_modules 27 | bower_components 28 | 29 | # Editors 30 | .idea 31 | *.iml 32 | 33 | # OS metadata 34 | .DS_Store 35 | Thumbs.db 36 | 37 | # Ignore built ts files 38 | dist/**/* 39 | 40 | test-dumps/ 41 | -------------------------------------------------------------------------------- /to-talkyard/README.md: -------------------------------------------------------------------------------- 1 | Usage: 2 | 3 | node to-talkyard/dist/to-talkyard/src/to-talkyard.js --help 4 | 5 | If importing from MySQL / PgSQL etc, maybe: https://github.com/tgriesser/knex 6 | 7 | 8 | Initiallly cloned from: https://github.com/Microsoft/TypeScript-Node-Starter, 9 | and everything now deleted except for things like tslint and some package.json settings. 10 | 11 | 12 | If later on there'll be many stand-alone packages like this, then consider 13 | using https://lernajs.io/ ? Maybe the slim-bundle, more-bundle etc could 14 | be their own Lerna packages, hmm. 15 | 16 | 17 | SECURITY [to_ty_risky] Any way to run to-talkyard in a sandbox? 18 | Nodejs code that parses user defined data = a tiny bit risky. 19 | -------------------------------------------------------------------------------- /to-talkyard/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | 'ts-jest': { 4 | tsConfigFile: 'tsconfig.json' 5 | } 6 | }, 7 | moduleFileExtensions: [ 8 | 'ts', 9 | 'js' 10 | ], 11 | transform: { 12 | '^.+\\.(ts|tsx)$': './node_modules/ts-jest/preprocessor.js' 13 | }, 14 | testMatch: [ 15 | '**/test/**/*.test.(ts|js)' 16 | ], 17 | testEnvironment: 'node' 18 | }; -------------------------------------------------------------------------------- /to-talkyard/n: -------------------------------------------------------------------------------- 1 | 2 | #comments_regex='s#^GET.*/-/assets/([0-9]+)/\*file\s+controllers.Assets.at.*$#\1#p' 3 | #current_version=`sed -nr "$find_version_regex" < conf/routes` 4 | 5 | 6 | sed -r 's,^\s*(#|//)(.*)$,,' < package.json 7 | 8 | 9 | -------------------------------------------------------------------------------- /to-talkyard/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "target": "es6", 6 | "noImplicitAny": true, 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "dist", 10 | "baseUrl": ".", 11 | "paths": { 12 | "*": [ 13 | "node_modules/*", 14 | "src/types/*" 15 | ] 16 | }, 17 | "types": [ 18 | "node", 19 | "assert", 20 | "core-js", 21 | "lodash"] 22 | 23 | }, 24 | "include": [ 25 | "src/**/*" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /translations/de_DE: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/de_DE -------------------------------------------------------------------------------- /translations/es_CL: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/es_CL -------------------------------------------------------------------------------- /translations/he_IL: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/he_IL -------------------------------------------------------------------------------- /translations/lv_LV: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/lv_LV -------------------------------------------------------------------------------- /translations/nl_NL: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/nl_NL -------------------------------------------------------------------------------- /translations/pl_PL: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/pl_PL -------------------------------------------------------------------------------- /translations/uk_UA: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/uk_UA -------------------------------------------------------------------------------- /translations/zh_CN: -------------------------------------------------------------------------------- 1 | ../modules/ty-translations/ui/zh_CN -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | v0.2025.008 2 | -------------------------------------------------------------------------------- /volumes/gulp-home/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debiki/talkyard/f2c0a8e46b1065717a9a6645c72f905007816230/volumes/gulp-home/.gitkeep -------------------------------------------------------------------------------- /wip/tags/tags-wip.txt: -------------------------------------------------------------------------------- 1 | 2 | Later: 3 | 4 | More data types, e.g. dates, date ranges, full text, geo locations, 5 | & ways to filter & search, e.g. all pages/events close to a city (coordinate). 6 | See Scala object TypeValueType. 7 | 8 | Distant future: 9 | 10 | [bottom_up_tags] 11 | Let people create their own tag types, own names. 12 | These will be scoped by user id, so won't be name conflicts (the db constraints 13 | in the tagtypes_t table support this alraedy, e.g.: tagtypes_u_anypat_urlslug). 14 | Later, moderators can promote these personal tag types to official tags 15 | (if the users want). 16 | 17 | --------------------------------------------------------------------------------