scriptorium 0.0.3 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.lt3 +324 -0
- data/README.md +3155 -1
- data/assets/.DS_Store +0 -0
- data/assets/README.md +44 -0
- data/assets/icons/social/reddit.png +0 -0
- data/assets/icons/social/x-logo.png +0 -0
- data/assets/icons/ui/.DS_Store +0 -0
- data/assets/icons/ui/back.png +0 -0
- data/assets/icons/ui/copy.png +0 -0
- data/assets/icons/ui/down.png +0 -0
- data/assets/icons/ui/end.png +0 -0
- data/assets/icons/ui/exit.png +0 -0
- data/assets/icons/ui/foo +10 -0
- data/assets/icons/ui/home.png +0 -0
- data/assets/icons/ui/left.png +0 -0
- data/assets/icons/ui/next.png +0 -0
- data/assets/icons/ui/right.png +0 -0
- data/assets/icons/ui/start.png +0 -0
- data/assets/icons/ui/up.png +0 -0
- data/assets/imagenotfound.jpg +0 -0
- data/assets/samples/placeholder.svg +9 -0
- data/assets/themes/standard/favicon.svg +6 -0
- data/bin/sblog +84 -5
- data/bin/scriptorium +1 -0
- data/doc/README.txt +6 -0
- data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +94 -0
- data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +2 -0
- data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +45 -0
- data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
- data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
- data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
- data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +35 -0
- data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +27 -0
- data/doc/anti-amnesia/20250807-213025.md +116 -0
- data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
- data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
- data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
- data/doc/banner_svg_config.md +114 -0
- data/doc/contrib.lt3 +8 -0
- data/doc/dependencies.md +281 -0
- data/doc/hacker.lt3 +5 -0
- data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
- data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
- data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
- data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
- data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
- data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
- data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
- data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
- data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
- data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
- data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
- data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
- data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
- data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
- data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
- data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
- data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
- data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
- data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
- data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
- data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
- data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
- data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
- data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
- data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
- data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
- data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
- data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
- data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
- data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
- data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
- data/doc/imported/0018-ruby-puns/post.html +46 -0
- data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
- data/doc/imported/0020-fffff/metadata.txt +7 -0
- data/doc/imported/0020-fffff/post.html +24 -0
- data/doc/imported/0020-fffff/source.lt3 +12 -0
- data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
- data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
- data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
- data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
- data/doc/imported/0023-doctor-sleep/post.html +63 -0
- data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
- data/doc/imported/0024-just-a-test/metadata.txt +7 -0
- data/doc/imported/0024-just-a-test/post.html +24 -0
- data/doc/imported/0024-just-a-test/source.lt3 +12 -0
- data/doc/imported/import_summary.txt +98 -0
- data/doc/livetext-informal-spec.txt +65 -0
- data/doc/myuserdoc/ch-0.lt3 +31 -0
- data/doc/myuserdoc/ch-1.lt3 +37 -0
- data/doc/myuserdoc/ch-10.lt3 +22 -0
- data/doc/myuserdoc/ch-2.lt3 +37 -0
- data/doc/myuserdoc/ch-3.lt3 +19 -0
- data/doc/myuserdoc/ch-4.lt3 +43 -0
- data/doc/myuserdoc/ch-5.lt3 +22 -0
- data/doc/myuserdoc/ch-6.lt3 +19 -0
- data/doc/myuserdoc/ch-7.lt3 +16 -0
- data/doc/myuserdoc/ch-8.lt3 +13 -0
- data/doc/myuserdoc/ch-9.lt3 +19 -0
- data/doc/myuserdoc/tweak.rb +18 -0
- data/doc/myuserdoc/userdoc-toc.txt +88 -0
- data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
- data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
- data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
- data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
- data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
- data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
- data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
- data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
- data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
- data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
- data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
- data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
- data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
- data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
- data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
- data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
- data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
- data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
- data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
- data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
- data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
- data/doc/old-posts/0024-just-a-test.lt3 +12 -0
- data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
- data/doc/old-repo +1 -0
- data/doc/reddit_credentials_template.json +8 -0
- data/doc/reddit_integration.md +207 -0
- data/doc/user.lt3 +35 -0
- data/doc/user_guide_section_1.md +137 -0
- data/doc/user_guide_section_10.md +515 -0
- data/doc/user_guide_section_11.md +708 -0
- data/doc/user_guide_section_2.md +233 -0
- data/doc/user_guide_section_3.md +5 -0
- data/doc/user_guide_section_4.md +221 -0
- data/doc/user_guide_section_5.md +243 -0
- data/doc/user_guide_section_6.md +147 -0
- data/doc/user_guide_section_7.md +311 -0
- data/doc/user_guide_section_8.md +224 -0
- data/doc/user_guide_section_9.md +375 -0
- data/lib/rouge/lexers/livetext.rb +74 -0
- data/lib/scriptorium/api.rb +2373 -0
- data/lib/scriptorium/banner_svg.rb +729 -0
- data/lib/scriptorium/contract.rb +34 -0
- data/lib/scriptorium/exceptions.rb +201 -1
- data/lib/scriptorium/helpers.rb +675 -0
- data/lib/scriptorium/post.rb +259 -0
- data/lib/scriptorium/reddit.rb +83 -0
- data/lib/scriptorium/repo.rb +938 -0
- data/lib/scriptorium/standard_files.rb +149 -0
- data/lib/scriptorium/support/bootstrap/css.txt +5 -0
- data/lib/scriptorium/support/bootstrap/js.txt +4 -0
- data/lib/scriptorium/support/common_js/clipboard.js +35 -0
- data/lib/scriptorium/support/common_js/content-loader.js +187 -0
- data/lib/scriptorium/support/common_js/navigation.js +52 -0
- data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
- data/lib/scriptorium/support/config/reddit.txt +10 -0
- data/lib/scriptorium/support/config/reddit_template.txt +17 -0
- data/lib/scriptorium/support/config/social.txt +8 -0
- data/lib/scriptorium/support/highlight/css.txt +2 -0
- data/lib/scriptorium/support/highlight/custom.css +119 -0
- data/lib/scriptorium/support/highlight/js.txt +1 -0
- data/lib/scriptorium/support/post_index/config.txt +15 -0
- data/lib/scriptorium/support/post_index/style.css +55 -0
- data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
- data/lib/scriptorium/support/templates/initial_post.lt3 +12 -0
- data/lib/scriptorium/support/templates/layout.txt +5 -0
- data/lib/scriptorium/support/templates/post.lt3 +104 -0
- data/lib/scriptorium/support/theme/footer.lt3 +2 -0
- data/lib/scriptorium/support/theme/header.lt3 +4 -0
- data/lib/scriptorium/support/theme/left.lt3 +3 -0
- data/lib/scriptorium/support/theme/main.lt3 +5 -0
- data/lib/scriptorium/support/theme/right.lt3 +3 -0
- data/lib/scriptorium/theme.rb +192 -0
- data/lib/scriptorium/version.rb +1 -1
- data/lib/scriptorium/view.rb +1021 -0
- data/lib/scriptorium/widgets/featured_posts.rb +149 -0
- data/lib/scriptorium/widgets/links.rb +112 -0
- data/lib/scriptorium/widgets/pages.rb +133 -0
- data/lib/scriptorium/widgets/widget.rb +133 -0
- data/lib/scriptorium.rb +38 -34
- data/lib/skeleton.rb +10 -1
- data/scriptorium.gemspec +17 -5
- data/test/README.md +69 -0
- data/test/WEB_INTEGRATION_README.md +196 -0
- data/test/all +83 -0
- data/test/api_demo.rb +99 -0
- data/test/assets/imagenotfound.jpg +0 -0
- data/test/assets/images/.DS_Store +0 -0
- data/test/assets/images/README.md +27 -0
- data/test/assets/images/odd_aspect.png +0 -0
- data/test/assets/images/perfect.png +0 -0
- data/test/assets/images/small.png +0 -0
- data/test/assets/images/tall.png +0 -0
- data/test/assets/images/very_tall.png +0 -0
- data/test/assets/images/very_wide.png +0 -0
- data/test/assets/images/wide.png +0 -0
- data/test/assets/testbanner.jpg +0 -0
- data/test/banner_svg/simple_helpers.rb +13 -0
- data/test/banner_svg/unit.rb +1000 -0
- data/test/config/deployment.txt +5 -0
- data/test/ed_test.rb +204 -0
- data/test/integration/cursor_banner_combinations.rb +193 -0
- data/test/integration/cursor_banner_features.rb +374 -0
- data/test/integration/integration_test.rb +326 -0
- data/test/integration/preview_flow_test.rb +94 -0
- data/test/livetext_plugin_test.rb +500 -0
- data/test/manual/asset_mgmt.rb +67 -0
- data/test/manual/banner-tests/index.html +45 -0
- data/test/manual/banner-tests/svg.txt +3 -0
- data/test/manual/banner-tests/test01.html +122 -0
- data/test/manual/banner-tests/test02.html +122 -0
- data/test/manual/banner-tests/test03.html +122 -0
- data/test/manual/banner-tests/test04.html +129 -0
- data/test/manual/banner-tests/test05.html +129 -0
- data/test/manual/banner-tests/test06.html +129 -0
- data/test/manual/banner-tests/test07.html +129 -0
- data/test/manual/banner-tests/test08.html +123 -0
- data/test/manual/banner-tests/test09.html +123 -0
- data/test/manual/banner-tests/test10.html +123 -0
- data/test/manual/banner-tests/test11.html +123 -0
- data/test/manual/banner-tests/test12.html +123 -0
- data/test/manual/banner-tests/test13.html +123 -0
- data/test/manual/banner-tests/test14.html +123 -0
- data/test/manual/banner-tests/test15.html +122 -0
- data/test/manual/banner-tests/test16.html +122 -0
- data/test/manual/banner-tests/test17.html +122 -0
- data/test/manual/banner-tests/test18.html +132 -0
- data/test/manual/banner-tests/test19.html +132 -0
- data/test/manual/banner-tests/test20.html +132 -0
- data/test/manual/banner-tests/test21.html +132 -0
- data/test/manual/banner-tests/test22.html +132 -0
- data/test/manual/banner-tests/test23.html +132 -0
- data/test/manual/banner-tests/test24.html +132 -0
- data/test/manual/banner-tests/test25.html +131 -0
- data/test/manual/banner_environment.rb +205 -0
- data/test/manual/codemirror_demo.html +773 -0
- data/test/manual/create_posts_for_web.rb +114 -0
- data/test/manual/environment.rb +67 -0
- data/test/manual/make_banner.rb +153 -0
- data/test/manual/preview_manual_test.rb +129 -0
- data/test/manual/sample_banner_config.txt +12 -0
- data/test/manual/test_advanced_widgets.rb +73 -0
- data/test/manual/test_banner_combinations.rb +120 -0
- data/test/manual/test_banner_features.rb +306 -0
- data/test/manual/test_banner_integration.rb +115 -0
- data/test/manual/test_banner_radial.rb +87 -0
- data/test/manual/test_basic_posts.rb +47 -0
- data/test/manual/test_layout_widgets.rb +40 -0
- data/test/manual/test_pagination.rb +24 -0
- data/test/manual/test_random_posts.rb +38 -0
- data/test/manual/test_syntax_highlighting.rb +167 -0
- data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
- data/test/rubytext/rubytext_demo_test.rb +42 -0
- data/test/rubytext/rubytext_testing_guide.md +277 -0
- data/test/run_automated_tests.rb +45 -0
- data/test/staging/.DS_Store +0 -0
- data/test/support/preview_utils.rb +88 -0
- data/test/syntax_highlighting_test.lt3 +124 -0
- data/test/test_gem_assets.rb +48 -0
- data/test/test_helpers.rb +240 -0
- data/test/tui_editor_integration_test.rb +296 -0
- data/test/tui_integration_test.rb +883 -0
- data/test/unit/api.rb +1776 -0
- data/test/unit/asset_management.rb +219 -0
- data/test/unit/backup_test.rb +451 -0
- data/test/unit/clipboard_test.rb +60 -0
- data/test/unit/contract_test.rb +69 -0
- data/test/unit/core.rb +1211 -0
- data/test/unit/deploy_config_test.rb +248 -0
- data/test/unit/deploy_test.rb +478 -0
- data/test/unit/edit_post_test.rb +168 -0
- data/test/unit/gem_asset_management.rb +183 -0
- data/test/unit/livetext_basic.rb +57 -0
- data/test/unit/livetext_compatibility.rb +82 -0
- data/test/unit/parse_cmd_test.rb +260 -0
- data/test/unit/permalink_copy_test.rb +211 -0
- data/test/unit/post.rb +309 -0
- data/test/unit/post_index_config_test.rb +258 -0
- data/test/unit/post_state_helpers_test.rb +137 -0
- data/test/unit/read_commented_file_test.rb +278 -0
- data/test/unit/reddit_test.rb +235 -0
- data/test/unit/repo.rb +569 -0
- data/test/unit/social_test.rb +366 -0
- data/test/unit/syntax_highlighting.rb +70 -0
- data/test/unit/theme_management_test.rb +91 -0
- data/test/unit/view.rb +498 -0
- data/test/unit/widgets.rb +669 -0
- data/test/web_integration_test.rb +231 -0
- data/test/web_test_helper.rb +218 -0
- data/test/web_workflow_test.rb +527 -0
- data/test/wizard_test.rb +123 -0
- data/ui/README.md +67 -0
- data/ui/common/lib/ui_common.rb +8 -0
- data/ui/rubytext/README.md +191 -0
- data/ui/rubytext/bin/scriptorium-rubytext +402 -0
- data/ui/rubytext/lib/rubytext_ui.rb +300 -0
- data/ui/tui/bin/scriptorium +1890 -0
- data/ui/tui/test/tui_test.rb +23 -0
- data/ui/web/app/app.rb +2600 -0
- data/ui/web/app/assets/livetext_mode.js +244 -0
- data/ui/web/app/error_helpers.rb +150 -0
- data/ui/web/app/views/advanced_config.erb +196 -0
- data/ui/web/app/views/asset_management.erb +645 -0
- data/ui/web/app/views/backup_management.erb +238 -0
- data/ui/web/app/views/banner_config.erb +200 -0
- data/ui/web/app/views/config_widget.erb +232 -0
- data/ui/web/app/views/configure_view.erb +401 -0
- data/ui/web/app/views/dashboard.erb +154 -0
- data/ui/web/app/views/deploy_config.erb +149 -0
- data/ui/web/app/views/edit_pages.erb +363 -0
- data/ui/web/app/views/edit_post.erb +175 -0
- data/ui/web/app/views/edit_theme.erb +73 -0
- data/ui/web/app/views/edit_theme_file.erb +74 -0
- data/ui/web/app/views/error_page.erb +29 -0
- data/ui/web/app/views/header_config.erb +155 -0
- data/ui/web/app/views/layout_config.erb +147 -0
- data/ui/web/app/views/navbar_config.erb +411 -0
- data/ui/web/app/views/theme_management.erb +130 -0
- data/ui/web/app/views/view_dashboard.erb +779 -0
- data/ui/web/app/views/widgets.erb +249 -0
- data/ui/web/bin/scriptorium-web +164 -0
- data/ui/web/test/web_basic_test.rb +38 -0
- data/ui/web/test_navbar.txt +7 -0
- data/ui/web/tmp/timing.log +17 -0
- data/ui/web/tmp/web_server.log +0 -0
- metadata +434 -8
- data/lib/scriptorium/engine.rb +0 -22
- data/test/engine/unit.rb +0 -44
@@ -0,0 +1,779 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Dashboard for <%= @current_view.name %> - Scriptorium</title>
|
5
|
+
<style>
|
6
|
+
body {
|
7
|
+
font-family: Arial, sans-serif;
|
8
|
+
margin: 0;
|
9
|
+
padding: 10px;
|
10
|
+
background-color: #f5f5f5;
|
11
|
+
}
|
12
|
+
.container {
|
13
|
+
}
|
14
|
+
.header {
|
15
|
+
padding: 0;
|
16
|
+
}
|
17
|
+
.header h1 {
|
18
|
+
margin: 0;
|
19
|
+
font-size: 28px;
|
20
|
+
line-height: 1;
|
21
|
+
}
|
22
|
+
.header p {
|
23
|
+
margin: 3px 0 0 0;
|
24
|
+
font-size: 16px;
|
25
|
+
opacity: 0.9;
|
26
|
+
}
|
27
|
+
.content {
|
28
|
+
padding: 10px;
|
29
|
+
}
|
30
|
+
.banner-section {
|
31
|
+
margin-bottom: 15px;
|
32
|
+
text-align: center;
|
33
|
+
}
|
34
|
+
.banner-section h2 {
|
35
|
+
margin-bottom: 8px;
|
36
|
+
color: #333;
|
37
|
+
}
|
38
|
+
.banner-display {
|
39
|
+
}
|
40
|
+
.actions-grid {
|
41
|
+
display: grid;
|
42
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
43
|
+
gap: 10px;
|
44
|
+
margin-bottom: 15px;
|
45
|
+
}
|
46
|
+
.action-card {
|
47
|
+
border: 1px solid #ddd;
|
48
|
+
border-radius: 6px;
|
49
|
+
padding: 12px;
|
50
|
+
text-align: center;
|
51
|
+
transition: all 0.2s;
|
52
|
+
}
|
53
|
+
.action-card:hover {
|
54
|
+
border-color: #007bff;
|
55
|
+
box-shadow: 0 1px 4px rgba(0,123,255,0.2);
|
56
|
+
}
|
57
|
+
.action-card h3 {
|
58
|
+
margin: 0 0 6px 0;
|
59
|
+
color: #333;
|
60
|
+
}
|
61
|
+
.action-card p {
|
62
|
+
margin: 0 0 8px 0;
|
63
|
+
color: #666;
|
64
|
+
font-size: 14px;
|
65
|
+
}
|
66
|
+
.btn {
|
67
|
+
display: inline-block;
|
68
|
+
padding: 4px 8px;
|
69
|
+
background: #007bff;
|
70
|
+
color: white;
|
71
|
+
text-decoration: none;
|
72
|
+
border-radius: 4px;
|
73
|
+
transition: background 0.2s;
|
74
|
+
font-size: 14px;
|
75
|
+
}
|
76
|
+
.btn:hover {
|
77
|
+
background: #0056b3;
|
78
|
+
}
|
79
|
+
.btn-secondary {
|
80
|
+
background: #6c757d;
|
81
|
+
}
|
82
|
+
.btn-secondary:hover {
|
83
|
+
background: #545b62;
|
84
|
+
}
|
85
|
+
.view-info {
|
86
|
+
background: #f8f9fa;
|
87
|
+
padding: 10px;
|
88
|
+
border-radius: 6px;
|
89
|
+
margin-bottom: 10px;
|
90
|
+
}
|
91
|
+
.view-info h3 {
|
92
|
+
margin: 0 0 6px 0;
|
93
|
+
color: #333;
|
94
|
+
}
|
95
|
+
.view-info p {
|
96
|
+
margin: 3px 0;
|
97
|
+
color: #666;
|
98
|
+
}
|
99
|
+
.config-buttons {
|
100
|
+
display: flex;
|
101
|
+
gap: 6px;
|
102
|
+
margin-bottom: 10px;
|
103
|
+
}
|
104
|
+
.button-group {
|
105
|
+
margin-top: 10px;
|
106
|
+
text-align: center;
|
107
|
+
}
|
108
|
+
|
109
|
+
/* Hover actions styles */
|
110
|
+
.actions-container {
|
111
|
+
position: relative;
|
112
|
+
display: inline-block;
|
113
|
+
}
|
114
|
+
|
115
|
+
.gear-icon {
|
116
|
+
font-size: 16px;
|
117
|
+
cursor: pointer;
|
118
|
+
color: #666;
|
119
|
+
display: inline-block;
|
120
|
+
height: 20px;
|
121
|
+
line-height: 20px;
|
122
|
+
vertical-align: middle;
|
123
|
+
}
|
124
|
+
|
125
|
+
.action-icons {
|
126
|
+
display: none;
|
127
|
+
position: absolute;
|
128
|
+
top: 50%;
|
129
|
+
right: 100%;
|
130
|
+
transform: translateY(-50%);
|
131
|
+
background: white;
|
132
|
+
border: 1px solid #ddd;
|
133
|
+
border-radius: 4px;
|
134
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
135
|
+
padding: 4px;
|
136
|
+
z-index: 1000;
|
137
|
+
flex-direction: column;
|
138
|
+
gap: 2px;
|
139
|
+
justify-content: center;
|
140
|
+
align-items: center;
|
141
|
+
min-width: 40px;
|
142
|
+
margin-right: 8px;
|
143
|
+
}
|
144
|
+
|
145
|
+
.action-icon {
|
146
|
+
font-size: 16px;
|
147
|
+
text-decoration: none;
|
148
|
+
margin: 0 1px;
|
149
|
+
transition: transform 0.2s;
|
150
|
+
display: inline-block;
|
151
|
+
height: 20px;
|
152
|
+
line-height: 20px;
|
153
|
+
vertical-align: middle;
|
154
|
+
}
|
155
|
+
|
156
|
+
.action-icon:hover {
|
157
|
+
transform: scale(1.2);
|
158
|
+
}
|
159
|
+
|
160
|
+
/* Show action icons on click, hide gear */
|
161
|
+
.actions-container.active .gear-icon {
|
162
|
+
display: none;
|
163
|
+
}
|
164
|
+
|
165
|
+
.actions-container.active .action-icons {
|
166
|
+
display: flex;
|
167
|
+
}
|
168
|
+
|
169
|
+
/* Fixed table column widths to prevent jittering */
|
170
|
+
.posts-table {
|
171
|
+
table-layout: fixed;
|
172
|
+
}
|
173
|
+
|
174
|
+
.posts-table th:nth-child(1), .posts-table td:nth-child(1) { width: 3%; } /* ID */
|
175
|
+
.posts-table th:nth-child(2), .posts-table td:nth-child(2) { width: 10%; } /* Published Date */
|
176
|
+
.posts-table th:nth-child(3), .posts-table td:nth-child(3) { width: 36%; } /* Title */
|
177
|
+
.posts-table th:nth-child(4), .posts-table td:nth-child(4) { width: 3%; } /* Deploy - very narrow for arrow */
|
178
|
+
.posts-table th:nth-child(5), .posts-table td:nth-child(5) { width: 10%; } /* Status */
|
179
|
+
.posts-table th:nth-child(6), .posts-table td:nth-child(6) { width: 20%; } /* Other Views */
|
180
|
+
.posts-table th:nth-child(7), .posts-table td:nth-child(7) { width: 5%; } /* Actions */
|
181
|
+
</style>
|
182
|
+
</head>
|
183
|
+
<body>
|
184
|
+
<div class="container">
|
185
|
+
<div class="header">
|
186
|
+
<h1 style="margin-left: 0.5em;">Dashboard for <%= @current_view.name %></h1>
|
187
|
+
</div>
|
188
|
+
|
189
|
+
<div class="content">
|
190
|
+
<div class="banner-section">
|
191
|
+
<div class="banner-display" id="header">
|
192
|
+
<%= @banner_svg %>
|
193
|
+
</div>
|
194
|
+
</div>
|
195
|
+
|
196
|
+
<div class="config-buttons">
|
197
|
+
<a href="#" class="btn" style="background: #17a2b8;" title="Preview locally" onclick="try{ showPreviewPopup(); }catch(e){}; window.open('/preview','_blank'); return false;">Preview</a>
|
198
|
+
<form method="post" action="/generate_view" style="display: inline;" onsubmit="showGeneratingPopup()">
|
199
|
+
<input type="hidden" name="view_name" value="<%= @current_view.name %>">
|
200
|
+
<button type="submit" class="btn" style="background: #007bff;">Generate</button>
|
201
|
+
</form>
|
202
|
+
<button type="button" class="btn" onclick="showUploadAssetForm()" style="background: #6c757d;">Upload Asset</button>
|
203
|
+
<a href="/asset_management" class="btn">Asset Management</a>
|
204
|
+
<a href="/backup_management" class="btn" style="background: #fd7e14;" title="Backup and restore repository">Backup/Restore</a>
|
205
|
+
<a href="/deploy" class="btn" style="background: #28a745;" title="Deploy to server" onclick="showUploadingPopup()">Deploy</a>
|
206
|
+
<a href="/browse" class="btn" style="background: #6f42c1;" title="Browse deployed site">Browse</a>
|
207
|
+
<a href="/advanced_config" class="btn btn-secondary">Advanced Config</a>
|
208
|
+
</div>
|
209
|
+
|
210
|
+
|
211
|
+
<!-- Upload Asset Modal -->
|
212
|
+
<div id="uploadAssetModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000;">
|
213
|
+
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; min-width: 400px;">
|
214
|
+
<h3>Upload Asset to Post</h3>
|
215
|
+
<form method="post" action="/upload_post_asset" enctype="multipart/form-data">
|
216
|
+
<div style="margin-bottom: 15px;">
|
217
|
+
<label for="post_id" style="display: block; margin-bottom: 5px; font-weight: bold;">Post ID:</label>
|
218
|
+
<select id="post_id" name="post_id" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
219
|
+
<option value="">Select a post...</option>
|
220
|
+
<% @posts.each do |post| %>
|
221
|
+
<option value="<%= post.id %>">#<%= post.id %> - <%= post.title %></option>
|
222
|
+
<% end %>
|
223
|
+
</select>
|
224
|
+
</div>
|
225
|
+
<div style="margin-bottom: 15px;">
|
226
|
+
<label for="asset_file" style="display: block; margin-bottom: 5px; font-weight: bold;">File:</label>
|
227
|
+
<input type="file" id="asset_file" name="file" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
228
|
+
</div>
|
229
|
+
<div style="text-align: right;">
|
230
|
+
<button type="button" onclick="hideUploadAssetForm()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Cancel</button>
|
231
|
+
<button type="submit" style="background: #007bff; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Upload Asset</button>
|
232
|
+
</div>
|
233
|
+
</form>
|
234
|
+
</div>
|
235
|
+
</div>
|
236
|
+
|
237
|
+
|
238
|
+
<% if params[:error] %>
|
239
|
+
<div class="alert alert-danger" style="background: #f8d7da; color: #721c24; padding: 10px; border-radius: 4px; margin: 10px 0; border: 1px solid #f5c6cb;">
|
240
|
+
<%= params[:error] %>
|
241
|
+
</div>
|
242
|
+
<% end %>
|
243
|
+
|
244
|
+
<!-- Create Post Button -->
|
245
|
+
<div style="margin-bottom: 20px;">
|
246
|
+
<button type="button" onclick="toggleCreatePostForm()" id="createPostButton" style="background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-weight: bold;">Create Post</button>
|
247
|
+
</div>
|
248
|
+
|
249
|
+
<!-- Create Post Section (Hidden by default) -->
|
250
|
+
<div id="createPostSection" class="create-post-section" style="display: none; margin-bottom: 20px; padding: 15px; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px;">
|
251
|
+
<h3 style="margin-top: 0;">Create New Post</h3>
|
252
|
+
<form method="post" action="/create_post" id="createPostForm">
|
253
|
+
<div style="display: flex; gap: 15px; align-items: end; flex-wrap: wrap;">
|
254
|
+
<div style="flex: 1; min-width: 200px;">
|
255
|
+
<label for="post_title" style="display: block; margin-bottom: 5px; font-weight: bold;">Title:</label>
|
256
|
+
<input type="text" id="post_title" name="title" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
257
|
+
</div>
|
258
|
+
<div style="flex: 1; min-width: 200px;">
|
259
|
+
<label for="post_blurb" style="display: block; margin-bottom: 5px; font-weight: bold;">Blurb:</label>
|
260
|
+
<input type="text" id="post_blurb" name="blurb" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
261
|
+
</div>
|
262
|
+
<div style="flex: 1; min-width: 200px;">
|
263
|
+
<label for="post_tags" style="display: block; margin-bottom: 5px; font-weight: bold;">Tags (comma-separated):</label>
|
264
|
+
<input type="text" id="post_tags" name="tags" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
265
|
+
</div>
|
266
|
+
</div>
|
267
|
+
|
268
|
+
<!-- View Selection -->
|
269
|
+
<div style="margin-top: 15px;">
|
270
|
+
<label style="display: block; margin-bottom: 8px; font-weight: bold;">Views:</label>
|
271
|
+
<div style="display: flex; gap: 15px; flex-wrap: wrap;">
|
272
|
+
<% @views.each do |view| %>
|
273
|
+
<label style="display: flex; align-items: center; gap: 5px; cursor: pointer;">
|
274
|
+
<input type="checkbox" name="views[]" value="<%= view.name %>"
|
275
|
+
<%= 'checked' if view.name == @current_view.name %>
|
276
|
+
style="margin: 0;">
|
277
|
+
<span><%= view.name %></span>
|
278
|
+
</label>
|
279
|
+
<% end %>
|
280
|
+
</div>
|
281
|
+
</div>
|
282
|
+
|
283
|
+
<!-- Form Buttons -->
|
284
|
+
<div style="margin-top: 15px; text-align: right;">
|
285
|
+
<button type="button" onclick="hideCreatePostForm()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Cancel</button>
|
286
|
+
<button type="submit" style="background: #28a745; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Edit</button>
|
287
|
+
</div>
|
288
|
+
</form>
|
289
|
+
</div>
|
290
|
+
|
291
|
+
<% if @posts && !@posts.empty? %>
|
292
|
+
<div class="posts-section">
|
293
|
+
<h3>Posts (<%= @total_posts %> total)</h3>
|
294
|
+
<!-- DEBUG: Total posts loaded: <%= @posts.length %>, Deleted posts: <%= @posts.select { |p| p.deleted }.length %> -->
|
295
|
+
<div class="posts-table">
|
296
|
+
<table class="posts-table" style="width: 90%; border-collapse: collapse; margin: 10px auto 10px 10px;">
|
297
|
+
<thead>
|
298
|
+
<tr style="background-color: #f2f2f2;">
|
299
|
+
<th style="padding: 6px; text-align: left; border: 1px solid #ddd;">ID</th>
|
300
|
+
<th style="padding: 6px; text-align: left; border: 1px solid #ddd;">Published</th>
|
301
|
+
<th style="padding: 6px; text-align: left; border: 1px solid #ddd; width: 36%;">Title</th>
|
302
|
+
<th style="padding: 6px; text-align: center; border: 1px solid #ddd;" title="Deployed? * = no"><svg width="28" height="28" viewBox="0 0 16 16" style="display: inline-block;"><polygon points="8,2 2,10 6,10 6,14 10,14 10,10 14,10" fill="#00aa00" stroke="#008800" stroke-width="0.5"/></svg></th>
|
303
|
+
<th style="padding: 6px; text-align: left; border: 1px solid #ddd; width: 10%;">Status</th>
|
304
|
+
<th style="padding: 6px; text-align: left; border: 1px solid #ddd; width: 20%;">Other Views</th>
|
305
|
+
<th style="padding: 6px; text-align: center; border: 1px solid #ddd; width: 5%;">Actions</th>
|
306
|
+
</tr>
|
307
|
+
</thead>
|
308
|
+
<tbody>
|
309
|
+
<% @posts.each do |post| %>
|
310
|
+
<tr style="<%= 'opacity: 0.6;' if post.deleted %>">
|
311
|
+
<!-- Col 1: ID -->
|
312
|
+
<td style="padding: 6px; border: 1px solid #ddd; font-family: monospace; text-align: right;"><%= post.id %></td>
|
313
|
+
<!-- Col 2: Published Date -->
|
314
|
+
<td style="padding: 6px; border: 1px solid #ddd;"><%= post.pubdate ? post.pubdate.split(' ').first : '—' %></td>
|
315
|
+
<!-- Col 3: Title -->
|
316
|
+
<td style="padding: 6px; border: 1px solid #ddd;">
|
317
|
+
<a href="#" onclick="openPostPreview(<%= post.id %>); return false;" style="color: #007bff; text-decoration: none; cursor: pointer; <%= 'text-decoration: line-through;' if post.deleted %>" title="Preview <%= post.title %>">
|
318
|
+
<%= post.title %>
|
319
|
+
</a>
|
320
|
+
</td>
|
321
|
+
<!-- Col 4: Deploy Status -->
|
322
|
+
<td style="padding: 6px; border: 1px solid #ddd; text-align: center;">
|
323
|
+
<% if post.deleted %>
|
324
|
+
<!-- Deleted posts don't show deployment status -->
|
325
|
+
<% elsif post.meta["post.deployed"] == "yes" %>
|
326
|
+
<!-- Deployed - show nothing -->
|
327
|
+
<% else %>
|
328
|
+
<!-- Not deployed - show red asterisk -->
|
329
|
+
<span style="color: #dc3545; font-weight: bold; font-size: 18px;">*</span>
|
330
|
+
<% end %>
|
331
|
+
</td>
|
332
|
+
<!-- Col 5: Publication Status -->
|
333
|
+
<td style="padding: 6px; border: 1px solid #ddd; text-align: left;">
|
334
|
+
<% if post.deleted %>
|
335
|
+
<span style="color: #dc3545; font-weight: bold;" title="Deleted">deleted</span>
|
336
|
+
<% elsif post.meta["post.published"] == "no" %>
|
337
|
+
<span style="color: #666;" title="Unpublished">unpublished</span>
|
338
|
+
<% elsif post.meta["post.published"] == "yes" %>
|
339
|
+
<span style="color: #28a745;" title="Published">published</span>
|
340
|
+
<% end %>
|
341
|
+
</td>
|
342
|
+
<!-- Col 6: Other Views -->
|
343
|
+
<td style="padding: 6px; border: 1px solid #ddd;">
|
344
|
+
<% other_views = post.views_array.reject { |v| v == @current_view.name } %>
|
345
|
+
<%= other_views.join(', ') %>
|
346
|
+
</td>
|
347
|
+
<!-- Col 7: Actions -->
|
348
|
+
<td style="padding: 6px; border: 1px solid #ddd; text-align: center;">
|
349
|
+
<div class="actions-container">
|
350
|
+
<span class="gear-icon">⚙️</span>
|
351
|
+
<div class="action-icons">
|
352
|
+
<a href="/edit_post/<%= post.id %>" class="action-icon" title="Edit">✏️</a>
|
353
|
+
<% if post.deleted %>
|
354
|
+
<form method="post" action="/restore_post/<%= post.id %>" style="display: inline;">
|
355
|
+
<button type="submit" class="action-icon" title="Restore" style="background: none; border: none; cursor: pointer; font-size: 16px;">🔄</button>
|
356
|
+
</form>
|
357
|
+
<% else %>
|
358
|
+
<form method="post" action="/delete_post/<%= post.id %>" style="display: inline;">
|
359
|
+
<button type="submit" class="action-icon" title="Delete" style="background: none; border: none; cursor: pointer; font-size: 16px;">🗑️</button>
|
360
|
+
</form>
|
361
|
+
<button type="button" class="action-icon toggle-status-btn" data-post-id="<%= post.id %>" title="<%= post.meta["post.published"] == "no" ? 'Publish' : 'Unpublish' %>" style="background: none; border: none; cursor: pointer; font-size: 14px; padding: 2px 6px; border-radius: 3px; color: #007bff;">
|
362
|
+
<% if post.meta["post.published"] == "no" %>
|
363
|
+
PUBLISH
|
364
|
+
<% else %>
|
365
|
+
UNPUBLISH
|
366
|
+
<% end %>
|
367
|
+
</button>
|
368
|
+
<% end %>
|
369
|
+
</div>
|
370
|
+
</div>
|
371
|
+
</td>
|
372
|
+
</tr>
|
373
|
+
<% end %>
|
374
|
+
</tbody>
|
375
|
+
</table>
|
376
|
+
</div>
|
377
|
+
|
378
|
+
<% if @total_pages > 1 %>
|
379
|
+
<div class="pagination" style="text-align: center; margin: 10px 0;">
|
380
|
+
<div style="display: flex; justify-content: center; gap: 3px; flex-wrap: wrap;">
|
381
|
+
<% if @current_page > 1 %>
|
382
|
+
<a href="/view/<%= @current_view.name %>?page=<%= @current_page - 1 %>" class="btn btn-secondary">« Previous</a>
|
383
|
+
<% end %>
|
384
|
+
|
385
|
+
<% 1.upto(@total_pages) do |page_num| %>
|
386
|
+
<% if page_num == @current_page %>
|
387
|
+
<span class="btn" style="background: #0056b3; cursor: default; font-size: 16px;"><%= page_num %></span>
|
388
|
+
<% else %>
|
389
|
+
<a href="/view/<%= @current_view.name %>?page=<%= page_num %>" class="btn"><%= page_num %></a>
|
390
|
+
<% end %>
|
391
|
+
<% end %>
|
392
|
+
|
393
|
+
<% if @current_page < @total_pages %>
|
394
|
+
<a href="/view/<%= @current_view.name %>?page=<%= @current_page + 1 %>" class="btn btn-secondary">Next »</a>
|
395
|
+
<% end %>
|
396
|
+
</div>
|
397
|
+
</div>
|
398
|
+
<% end %>
|
399
|
+
</div>
|
400
|
+
<% else %>
|
401
|
+
<div class="posts-section">
|
402
|
+
<h3>Posts</h3>
|
403
|
+
<p>No posts found in this view.</p>
|
404
|
+
</div>
|
405
|
+
<% end %>
|
406
|
+
|
407
|
+
<div class="button-group">
|
408
|
+
<a href="/" class="btn btn-secondary">Back to Main Dashboard</a>
|
409
|
+
</div>
|
410
|
+
</div>
|
411
|
+
</div>
|
412
|
+
|
413
|
+
<script>
|
414
|
+
// Global functions accessible from HTML onclick attributes
|
415
|
+
function ensureOverlay(id, text, bg) {
|
416
|
+
var el = document.getElementById(id);
|
417
|
+
if (!el) {
|
418
|
+
el = document.createElement('div');
|
419
|
+
el.id = id;
|
420
|
+
el.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 16px 22px; border-radius: 8px; z-index: 9999; color: white; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.3);';
|
421
|
+
document.body.appendChild(el);
|
422
|
+
}
|
423
|
+
el.style.background = bg;
|
424
|
+
el.textContent = text;
|
425
|
+
el.style.display = 'block';
|
426
|
+
return el;
|
427
|
+
}
|
428
|
+
function showPreviewPopup() { try { ensureOverlay('preview-popup', 'Opening preview...', '#17a2b8'); } catch(e) {} }
|
429
|
+
|
430
|
+
function toggleCreatePostForm() {
|
431
|
+
console.log('toggleCreatePostForm called');
|
432
|
+
let form = document.getElementById('createPostSection');
|
433
|
+
let button = document.getElementById('createPostButton');
|
434
|
+
console.log('Form element:', form);
|
435
|
+
console.log('Button element:', button);
|
436
|
+
console.log('Form display style:', form.style.display);
|
437
|
+
|
438
|
+
if (form.style.display === 'none' || form.style.display === '') {
|
439
|
+
form.style.display = 'block';
|
440
|
+
button.textContent = 'Hide';
|
441
|
+
console.log('Showing form');
|
442
|
+
} else {
|
443
|
+
form.style.display = 'none';
|
444
|
+
button.textContent = 'Create Post';
|
445
|
+
console.log('Hiding form');
|
446
|
+
}
|
447
|
+
}
|
448
|
+
|
449
|
+
function hideCreatePostForm() {
|
450
|
+
let form = document.getElementById('createPostSection');
|
451
|
+
let button = document.getElementById('createPostButton');
|
452
|
+
form.style.display = 'none';
|
453
|
+
button.textContent = 'Create Post';
|
454
|
+
}
|
455
|
+
|
456
|
+
function openEditModal(postId) {
|
457
|
+
// Hide the create post form
|
458
|
+
hideCreatePostForm();
|
459
|
+
|
460
|
+
// Show the edit modal
|
461
|
+
let modal = document.getElementById('editPostModal');
|
462
|
+
if (modal) {
|
463
|
+
modal.style.display = 'block';
|
464
|
+
// Load post content via AJAX
|
465
|
+
loadPostContent(postId);
|
466
|
+
}
|
467
|
+
}
|
468
|
+
|
469
|
+
function closeEditModal() {
|
470
|
+
let modal = document.getElementById('editPostModal');
|
471
|
+
if (modal) {
|
472
|
+
modal.style.display = 'none';
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
function loadPostContent(postId) {
|
477
|
+
// Set the post ID in the form
|
478
|
+
document.getElementById('editPostId').value = postId;
|
479
|
+
|
480
|
+
// Set the form action to the correct route
|
481
|
+
document.getElementById('editPostForm').action = `/save_post/${postId}`;
|
482
|
+
|
483
|
+
// Load post content via AJAX
|
484
|
+
fetch(`/api/post_content/${postId}`)
|
485
|
+
.then(response => response.text())
|
486
|
+
.then(content => {
|
487
|
+
if (editor) {
|
488
|
+
editor.setValue(content);
|
489
|
+
} else {
|
490
|
+
document.getElementById('editContent').value = content;
|
491
|
+
}
|
492
|
+
})
|
493
|
+
.catch(error => {
|
494
|
+
console.error('Error loading post content:', error);
|
495
|
+
document.getElementById('editContent').value = 'Error loading post content.';
|
496
|
+
});
|
497
|
+
}
|
498
|
+
|
499
|
+
function showUploadAssetForm() {
|
500
|
+
document.getElementById('uploadAssetModal').style.display = 'block';
|
501
|
+
}
|
502
|
+
|
503
|
+
function hideUploadAssetForm() {
|
504
|
+
document.getElementById('uploadAssetModal').style.display = 'none';
|
505
|
+
}
|
506
|
+
|
507
|
+
function showUploadingPopup() {
|
508
|
+
console.log('showUploadingPopup called');
|
509
|
+
let popup = document.getElementById('uploading-popup');
|
510
|
+
if (!popup) {
|
511
|
+
popup = document.createElement('div');
|
512
|
+
popup.id = 'uploading-popup';
|
513
|
+
popup.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px 30px; border-radius: 8px; z-index: 1000; font-size: 16px; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.3); background: #17a2b8; color: white;';
|
514
|
+
popup.textContent = 'Uploading...';
|
515
|
+
document.body.appendChild(popup);
|
516
|
+
console.log('Created uploading popup');
|
517
|
+
}
|
518
|
+
popup.style.display = 'block';
|
519
|
+
console.log('Uploading popup should be visible');
|
520
|
+
}
|
521
|
+
|
522
|
+
function hideUploadingPopup() {
|
523
|
+
console.log('hideUploadingPopup called');
|
524
|
+
let popup = document.getElementById('uploading-popup');
|
525
|
+
if (popup) {
|
526
|
+
popup.style.display = 'none';
|
527
|
+
console.log('Uploading popup hidden');
|
528
|
+
} else {
|
529
|
+
console.log('No uploading popup found to hide');
|
530
|
+
}
|
531
|
+
}
|
532
|
+
|
533
|
+
function openPostPreview(postId) {
|
534
|
+
// Get the post data from the table to find the title and construct the slug
|
535
|
+
const postRow = document.querySelector(`a[onclick*="openPostPreview(${postId})"]`);
|
536
|
+
if (!postRow) {
|
537
|
+
console.error('Could not find post row for ID:', postId);
|
538
|
+
alert('Error: Could not find post');
|
539
|
+
return;
|
540
|
+
}
|
541
|
+
const postTitle = postRow.textContent.trim();
|
542
|
+
|
543
|
+
// Construct the expected filename based on the pattern we saw
|
544
|
+
const paddedId = postId.toString().padStart(4, '0');
|
545
|
+
const slug = postTitle.toLowerCase()
|
546
|
+
.replace(/[^a-z0-9\s-]/g, '') // Remove special chars
|
547
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
548
|
+
.replace(/-+/g, '-') // Replace multiple hyphens with single
|
549
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
550
|
+
|
551
|
+
const filename = `${paddedId}-${slug}.html`;
|
552
|
+
const currentView = '<%= @current_view.name %>';
|
553
|
+
// Open the individual post file directly for the current view
|
554
|
+
const url = `/preview/${currentView}/posts/${filename}`;
|
555
|
+
|
556
|
+
// Open in a new window
|
557
|
+
window.open(url, '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes');
|
558
|
+
}
|
559
|
+
|
560
|
+
|
561
|
+
document.addEventListener('DOMContentLoaded', function() {
|
562
|
+
// Check if we should open the edit modal
|
563
|
+
const urlParams = new URLSearchParams(window.location.search);
|
564
|
+
const editPostId = urlParams.get('edit_post');
|
565
|
+
if (editPostId) {
|
566
|
+
openEditModal(editPostId);
|
567
|
+
// Clean up URL to remove edit_post parameter
|
568
|
+
const newUrl = window.location.pathname + (urlParams.get('message') ? '?message=' + encodeURIComponent(urlParams.get('message')) : '');
|
569
|
+
window.history.replaceState({}, document.title, newUrl);
|
570
|
+
}
|
571
|
+
|
572
|
+
// Handle status toggle buttons
|
573
|
+
document.querySelectorAll('.toggle-status-btn').forEach(function(btn) {
|
574
|
+
btn.addEventListener('click', function() {
|
575
|
+
const postId = this.getAttribute('data-post-id');
|
576
|
+
const button = this;
|
577
|
+
|
578
|
+
// Show loading state
|
579
|
+
button.style.opacity = '0.5';
|
580
|
+
|
581
|
+
fetch('/toggle_post_status/' + postId, {
|
582
|
+
method: 'POST',
|
583
|
+
headers: {
|
584
|
+
'Content-Type': 'application/json',
|
585
|
+
}
|
586
|
+
})
|
587
|
+
.then(response => response.json())
|
588
|
+
.then(data => {
|
589
|
+
if (data.success) {
|
590
|
+
// Update the button text and title
|
591
|
+
if (data.published) {
|
592
|
+
button.innerHTML = 'UNPUBLISH';
|
593
|
+
button.title = 'Unpublish';
|
594
|
+
button.setAttribute('data-published', 'true');
|
595
|
+
} else {
|
596
|
+
button.innerHTML = 'PUBLISH';
|
597
|
+
button.title = 'Publish';
|
598
|
+
button.setAttribute('data-published', 'false');
|
599
|
+
}
|
600
|
+
|
601
|
+
// Update the status column
|
602
|
+
const statusCell = button.closest('tr').querySelector('td:nth-child(4)');
|
603
|
+
console.log('Status cell found:', statusCell);
|
604
|
+
console.log('Current status cell content:', statusCell.innerHTML);
|
605
|
+
|
606
|
+
if (data.published) {
|
607
|
+
// Published - show published status
|
608
|
+
statusCell.innerHTML = '<span style="color: #28a745;" title="Published">published</span>';
|
609
|
+
console.log('Status updated to published');
|
610
|
+
} else {
|
611
|
+
// Unpublished - show unpublished status
|
612
|
+
statusCell.innerHTML = '<span style="color: #666;" title="Unpublished">unpublished</span>';
|
613
|
+
console.log('Status updated to unpublished');
|
614
|
+
}
|
615
|
+
|
616
|
+
// Also update the button's data attribute for future toggles
|
617
|
+
button.setAttribute('data-published', data.published.toString());
|
618
|
+
|
619
|
+
// Show success message temporarily
|
620
|
+
showMessage(data.message, 'success');
|
621
|
+
} else {
|
622
|
+
showMessage('Failed to toggle status', 'error');
|
623
|
+
}
|
624
|
+
})
|
625
|
+
.catch(error => {
|
626
|
+
console.error('Error:', error);
|
627
|
+
showMessage('Failed to toggle status', 'error');
|
628
|
+
})
|
629
|
+
.finally(() => {
|
630
|
+
button.style.opacity = '1';
|
631
|
+
});
|
632
|
+
});
|
633
|
+
});
|
634
|
+
|
635
|
+
function showMessage(message, type) {
|
636
|
+
// Create or update message element
|
637
|
+
let messageEl = document.getElementById('status-message');
|
638
|
+
if (!messageEl) {
|
639
|
+
messageEl = document.createElement('div');
|
640
|
+
messageEl.id = 'status-message';
|
641
|
+
messageEl.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 15px 25px; border-radius: 8px; z-index: 1000; font-size: 16px; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.3);';
|
642
|
+
document.body.appendChild(messageEl);
|
643
|
+
}
|
644
|
+
|
645
|
+
messageEl.textContent = message;
|
646
|
+
messageEl.style.background = type === 'success' ? '#28a745' : '#dc3545';
|
647
|
+
messageEl.style.color = 'white';
|
648
|
+
|
649
|
+
// Hide after 3 seconds
|
650
|
+
setTimeout(() => {
|
651
|
+
messageEl.style.display = 'none';
|
652
|
+
}, 3000);
|
653
|
+
}
|
654
|
+
|
655
|
+
|
656
|
+
|
657
|
+
// Check for deployment success message in URL parameters
|
658
|
+
const deployParams = new URLSearchParams(window.location.search);
|
659
|
+
const deployMessage = deployParams.get('deploy_success');
|
660
|
+
const hideUploading = deployParams.get('hide_uploading');
|
661
|
+
|
662
|
+
if (deployMessage) {
|
663
|
+
showMessage(deployMessage, 'success');
|
664
|
+
// Clean up URL
|
665
|
+
const newUrl = window.location.pathname;
|
666
|
+
window.history.replaceState({}, document.title, newUrl);
|
667
|
+
}
|
668
|
+
|
669
|
+
// Clean up any message parameters
|
670
|
+
const message = urlParams.get('message');
|
671
|
+
if (message) {
|
672
|
+
showMessage(message, 'success');
|
673
|
+
// Clean up URL after showing message
|
674
|
+
setTimeout(() => {
|
675
|
+
const newUrl = window.location.pathname;
|
676
|
+
window.history.replaceState({}, document.title, newUrl);
|
677
|
+
}, 1000);
|
678
|
+
}
|
679
|
+
|
680
|
+
// Hide uploading popup if deployment completed
|
681
|
+
if (hideUploading) {
|
682
|
+
hideUploadingPopup();
|
683
|
+
}
|
684
|
+
|
685
|
+
// Add click handlers for action containers
|
686
|
+
document.querySelectorAll('.actions-container').forEach(container => {
|
687
|
+
container.addEventListener('click', function(e) {
|
688
|
+
e.stopPropagation();
|
689
|
+
// Close all other action containers
|
690
|
+
document.querySelectorAll('.actions-container.active').forEach(active => {
|
691
|
+
if (active !== this) {
|
692
|
+
active.classList.remove('active');
|
693
|
+
}
|
694
|
+
});
|
695
|
+
// Toggle this container
|
696
|
+
this.classList.toggle('active');
|
697
|
+
});
|
698
|
+
});
|
699
|
+
|
700
|
+
// Close action containers when clicking elsewhere
|
701
|
+
document.addEventListener('click', function() {
|
702
|
+
document.querySelectorAll('.actions-container.active').forEach(container => {
|
703
|
+
container.classList.remove('active');
|
704
|
+
});
|
705
|
+
});
|
706
|
+
});
|
707
|
+
</script>
|
708
|
+
|
709
|
+
<!-- Edit Post Modal -->
|
710
|
+
<div id="editPostModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000;">
|
711
|
+
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 8px; width: 80vw; height: 80vh; max-width: 1200px; max-height: 800px; display: flex; flex-direction: column;">
|
712
|
+
<!-- Modal Header -->
|
713
|
+
<div style="padding: 20px; border-bottom: 1px solid #ddd;">
|
714
|
+
<h2 style="margin: 0;">Edit Post</h2>
|
715
|
+
</div>
|
716
|
+
|
717
|
+
<!-- Modal Content -->
|
718
|
+
<div style="flex: 1; padding: 20px; overflow: hidden;">
|
719
|
+
<form id="editPostForm" method="post" onsubmit="syncCodeMirrorContent()">
|
720
|
+
<input type="hidden" id="editPostId" name="post_id" value="">
|
721
|
+
<div style="height: 100%; display: flex; flex-direction: column;">
|
722
|
+
<label for="editContent" style="margin-bottom: 10px; font-weight: bold;">Post Content:</label>
|
723
|
+
<textarea name="content" id="editContent" placeholder="Enter your post content here..." style="flex: 1; font-family: monospace; font-size: 14px; border: 1px solid #ddd; border-radius: 4px; padding: 10px; resize: none;"></textarea>
|
724
|
+
</div>
|
725
|
+
</form>
|
726
|
+
</div>
|
727
|
+
|
728
|
+
<!-- Modal Footer -->
|
729
|
+
<div style="padding: 20px; border-top: 1px solid #ddd; text-align: right;">
|
730
|
+
<button type="button" onclick="closeEditModal()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Cancel</button>
|
731
|
+
<button type="submit" form="editPostForm" style="background: #28a745; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Save Post</button>
|
732
|
+
</div>
|
733
|
+
</div>
|
734
|
+
</div>
|
735
|
+
|
736
|
+
<!-- CodeMirror CSS and JS -->
|
737
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
|
738
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/monokai.min.css">
|
739
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
|
740
|
+
|
741
|
+
<script>
|
742
|
+
let editor = null;
|
743
|
+
|
744
|
+
function syncCodeMirrorContent() {
|
745
|
+
if (editor) {
|
746
|
+
document.getElementById('editContent').value = editor.getValue();
|
747
|
+
}
|
748
|
+
}
|
749
|
+
|
750
|
+
|
751
|
+
// Initialize CodeMirror when modal opens
|
752
|
+
function initCodeMirror() {
|
753
|
+
if (!editor) {
|
754
|
+
editor = CodeMirror.fromTextArea(document.getElementById('editContent'), {
|
755
|
+
mode: 'livetext',
|
756
|
+
theme: 'monokai',
|
757
|
+
lineNumbers: true,
|
758
|
+
lineWrapping: false,
|
759
|
+
indentUnit: 2,
|
760
|
+
tabSize: 2,
|
761
|
+
indentWithTabs: false,
|
762
|
+
scrollbarStyle: 'native',
|
763
|
+
viewportMargin: Infinity
|
764
|
+
});
|
765
|
+
|
766
|
+
// Hide the original textarea
|
767
|
+
document.getElementById('editContent').style.display = 'none';
|
768
|
+
}
|
769
|
+
}
|
770
|
+
|
771
|
+
// Override openEditModal to initialize CodeMirror
|
772
|
+
const originalOpenEditModal = openEditModal;
|
773
|
+
openEditModal = function(postId) {
|
774
|
+
originalOpenEditModal(postId);
|
775
|
+
setTimeout(initCodeMirror, 100); // Small delay to ensure modal is visible
|
776
|
+
};
|
777
|
+
</script>
|
778
|
+
</body>
|
779
|
+
</html>
|