A custom search mod for SMF, adding forum-wide post search ("ISE"), PDF-attachment search ("ISEpdf"), and text-attachment search ("ISEmisc") as options in the existing CustomSearch dropdown.
.md/.txt/.text/.json/ .html โ both content and filename/topic subject; .html has its tags/scripts/styles stripped before indexing so only visible text is searched) plus a much wider set of code/config attachment types by filename and file type only (.py/.php/.js/.sh/.sql/.css/ .c/.cpp/.java/.rb/.go/.rs/.pl/.xml/.yml/.yaml/.ini/ .conf โ no content read, no full-text search, just viewable via the "Open file" link and filename/topic searchable), with results linking straight to the owning forum post.All three bypass the database at query time โ they read pre-built JSON indexes for speed, so the indexes must be rebuilt manually after posts or attachments change (see ยง4).
<SMF root>/
โโโ Sources/Search.php โ dispatcher: case 'ISE' / 'ISEpdf' / 'ISEmisc'
โโโ Sources/ManageSettings.php โ CustomSearch_use_ISE / _ISEpdf / _ISEmisc
โ checkboxes, plus ise_reset_pdfjs /
โ ise_reset_isedata reset checkboxes
โโโ Sources/Admin.php โ registers CustomSearch in admin menu
โโโ Themes/default/index.template.php โ dropdown options
โโโ Themes/default/languages/Modifications.english.php โ label strings
โ (also patched at Themes/default/language/ โ singular โ for installs
โ that use that spelling instead)
โโโ ISE_Data/ โ generated indexes + logs (777, apache-owned)
โ โโโ word_index.json / post_store.json โ forum post index
โ โโโ word_index_pdf.json / pdf_store.json โ PDF index
โ โโโ word_index_misc.json / misc_store.json โ Misc (.md/.txt/.text/.json/.html content, plus filename-only code/config types) index
โ โโโ ise_trace.log โ shared tracing log, all sources
โ โโโ *.log โ other trace/debug logs
โโโ The_ISE_Project/ โ all scripts
โ โโโ qf_search_bridge.php โ web entry point for ISE
โ โโโ qf_PDFsearch_bridge.php โ web entry point for ISEpdf
โ โโโ qf_Miscsearch_bridge.php โ web entry point for ISEmisc
โ โโโ search.py โ forum post search (called by bridge)
โ โโโ PDFsearch.py โ PDF search (called by bridge)
โ โโโ MiscSearch.py โ Misc search (called by bridge)
โ โโโ ISE_help.html โ shared help page, all three sources
โ โ (see ยง8) โ served on "/help" query,
โ โ linked from every results page footer
โ โโโ IndexBuilder.py โ builds word_index.json / post_store.json
โ โโโ PDFIndexer.py โ builds word_index_pdf.json / pdf_store.json
โ โโโ MiscIndexer.py โ builds word_index_misc.json / misc_store.json
โ โโโ IndexLookup.py โ shared index-reading helper
โ โโโ ise_trace.py โ shared tracing module, all three sources
โ โโโ reset_ise_data.php โ install-time code hook, see ยง3
โ โโโ RankingEngine.py, QueryParser.py, etc. โ scoring/parsing internals
โโโ pdfjs/ โ bundled PDF.js viewer
Each SMF instance (live, smf20, smf21, ...) has its own complete, independent copy of The_ISE_Project/ and ISE_Data/ โ nothing is shared between instances by design.
readme.txt in the package): Board-color-and-icons, Automatic Package Version Emulation, CustomSearch.ISE_V<version>.zip via SMF's package manager as normal.install_ise.php runs automatically and creates The_ISE_Project/ and ISE_Data/ with 777 permissions, then kicks off IndexBuilder.py, PDFIndexer.py, and MiscIndexer.py in the background (non-blocking โ the install request itself returns immediately). Progress/output for each lands in its own log file under ISE_Data/.Uninstalling reverses the install.xml file edits and deletes The_ISE_Project/ only. ISE_Data/ and pdfjs/ are deliberately left untouched on uninstall โ both hold real generated data (indexes, trace logs, PDF.js viewer assets) that shouldn't vanish just because the mod entry is removed from Package Manager's list.
cd <SMF root>/The_ISE_Project
python3 IndexBuilder.py --monitor # forum posts โ word_index.json / post_store.json
python3 PDFIndexer.py --monitor # PDF attachments โ word_index_pdf.json / pdf_store.json
python3 MiscIndexer.py --monitor # .md/.txt/.text/.json โ word_index_misc.json / misc_store.json
IndexBuilder.py and MiscIndexer.py are both full-rebuild-only โ no resume/incremental mode, safe to re-run anytime. PDFIndexer.py has its own resume-by-default behavior; see the Python Scripts Manual for details.
Suggested cron schedule (adjust to your forum's actual posting/upload volume):
# Every 2 hours (on the hour) โ rebuild the Post index
0 */2 * * * python3 /var/www/html/The_ISE_Project/IndexBuilder.py >> /var/www/html/ISE_Data/indexbuilder.log 2>&1
# Every 2 hours (on the half-hour) โ rebuild the Misc/text index
30 */2 * * * python3 /var/www/html/The_ISE_Project/MiscIndexer.py --monitor >> /var/www/html/ISE_Data/misc_indexer.log 2>&1
# Every 6 hours โ fast rebuild of the PDF index
5 */6 * * * python3 /var/www/html/The_ISE_Project/PDFIndexer.py --fast --monitor >> /var/www/html/ISE_Data/pdfindexer_fast.log 2>&1
# Every 2 days โ full union rebuild (both extractors) of the PDF index
10 0 */2 * * python3 /var/www/html/The_ISE_Project/PDFIndexer.py --monitor >> /var/www/html/ISE_Data/pdfindexer_union.log 2>&1
Two checkboxes on the CustomSearch settings page control a one-shot, self-clearing reset mechanism for the next install/reinstall:
pdfjs/ on the next install. Since the library refresh itself already happens on every normal install (see ยง3), this checkbox only matters when you want a genuine clean slate โ including clearing out anything not part of the vendored library, such as custom "ln" symlinks. Ticking this means you'll need to re-establish those links afterward โ the checkbox label says so directly.ISE_Data/ on the next install, wiping all three indexes and the trace log. You'll need to rebuild all three indexes from scratch afterward (ยง4).Both default to unchecked. Each is a one-shot flag: tick it, save, run the install/reinstall, and it clears itself back to unchecked automatically once it fires โ so it can't accidentally wipe data again on a later reinstall you didn't intend to reset.
Supported for ISEpdf and ISEmisc; not yet for ISE (forum posts) โ see the in-app help page for the user-facing explanation of */? syntax and per-source behavior: <boardurl>/The_ISE_Project/ISE_help.html, or type /help in any of the three search boxes (ยง8).
Implementation note: a term with no wildcard characters still uses the original fast exact-match (content) / substring-match (filename/topic) path in both scripts โ wildcards only trigger the slower full-vocabulary scan when actually present in the term. ISE (posts) doesn't have this yet because its matching runs through QueryParser.py โ Tokeniser.py โ ANDMatcher.py โ IndexLookup.py, a different pipeline from ISEpdf/ISEmisc's direct dict-lookup approach โ wildcard support there needs its own implementation against those modules, not a copy of the ISEpdf/ISEmisc approach.
position="before"/position="after" are inverted in this fork's package manager compared to stock SMF โ before actually inserts content after the matched text, and vice versa. install.xml is already written to account for this; don't "fix" it back to stock semantics.<search>/<replace> (no position attribute) silently no-ops โ always use position="before|after|replace" + <add> instead, even though the install preview shows "Test successful" either way.pdftotext or similar must pass encoding="utf-8", errors="replace" explicitly, or it will crash on real-world PDFs containing accented characters, curly quotes, em-dashes, etc.<search position="before">/<add> entries one at a time against the same target line, over several install cycles, tends to leave that line's whitespace/alignment inconsistent โ array entries and $txt[...] assignments end up crammed onto one line with no newlines between them. The next mod's search then silently fails to match ("Skipping Search"), because it's looking for clean, single-purpose text that no longer exists in that form. Prefer whole-block position="replace" over incremental adds whenever a settings array or a labeled string block (like the BIOHAZARD:CustomSearch block in Modifications.english.php) is likely to be touched by more than one install over its lifetime โ replace the entire known block in one atomic operation instead of layering adds onto it.install.xml (a <modification>-type document) must only contain <file>/<operation> blocks. <uninstall>, <require-file>, <require-dir>, and <code> tags belong exclusively in package-info.xml โ a stray <uninstall> block was found duplicated inside install.xml itself in an earlier version (including a nonsensical self-reference back to install.xml), which is invalid structure for that file type and was removed.require-dir on install merges, it never deletes. Any file already present in the destination stays untouched unless the package's own zip contains a file with that exact same name, in which case only that one file gets overwritten. This means the ISE_Data/ folder packed inside any zip must contain only .htaccess โ never real index/store/log filenames (word_index*.json, *_store.json, ise_trace.log) โ or a completely routine reinstall would silently overwrite live search data with the package's bundled (empty/stale) copies, with no checkbox or warning involved. pdfjs/, by contrast, should contain the full library every time โ that's vendored code meant to refresh on every install.<!DOCTYPE package-info SYSTEM "http://..."> external DTD reference can fail to resolve depending on the server's libxml/PHP config (blocked outbound access, stricter defaults) โ SMF doesn't need the DOCTYPE to parse the file, so it's safe to drop.zip -X -D (no extended attributes, no directory entries) โ this keeps the archive at plain PKZIP 2.0 (extract version 20), no Zip64./help and the footer link)All three search UIs share one built-in help page, The_ISE_Project/ISE_help.html โ a single static file (web-accessible under The_ISE_Project/ per the .htaccess *.html rule) that explains each source's search methods and, accurately, how unquoted/'single'/ "double" quoting and */? wildcards behave differently across ISE, ISEpdf, and ISEmisc (including the real gap where single-quoting a word in ISE search returns nothing โ documented plainly rather than glossed over). It carries its own theme switcher (same 13 themes as the results pages) and picks up whichever theme was last selected on any of the three sources' results pages.
Two ways to reach it:
/help (unquoted, case/whitespace-insensitive) as the query in any of the three search boxes. search.py, PDFsearch.py, and MiscSearch.py all detect this exact query and short-circuit before running any real search โ they copy ISE_help.html's content straight into whatever --html path the PHP bridge requested, so the normal bridge readfile() flow serves it with no PHP changes needed./help keyword exists.| Symptom | Likely cause |
|---|---|
| Same search results regardless of query | Bridge PHP silently failed and served a stale cached output file |
| PDF search always returns โค20 results | --limit cap in PDFsearch.py (should default to unlimited) |
| Search returns almost nothing on a populated forum | Index wasn't rebuilt after content changed, or IndexBuilder.py/PDFIndexer.py/MiscIndexer.py pointed at the wrong instance's Settings.php/attachments folder |
| One instance's search shows another instance's data | A script has a hardcoded absolute path instead of resolving relative to its own file location โ check ATTACH_DIR, WORD_INDEX_PATH, *_STORE_PATH, and the bridge's $script variable |
| Dropdown missing ISE/ISEpdf/ISEmisc options after install | index.template.php operation didn't match โ check tab count/whitespace against the live file with cat -A |
install_ise.php runs but nothing happens |
Check file_exists() on the target script paths โ likely means The_ISE_Project wasn't extracted yet (code step must run after the extract steps in package-info.xml) |
| Checkbox appears on the settings page but with no label text (blank) | The language-string <search> anchor in install.xml/a mini-mod didn't match โ usually a whitespace/alignment mismatch against the live file's actual (often hand-edited, inconsistently spaced) content. Always verify the anchor byte-for-byte against a fresh copy of the live file (cat -A or od -c), not a pasted/remembered version of it |
| Package Manager shows "Skipping Search" for a file operation | The <search> text genuinely wasn't found in the target file at install time โ pull the file straight off the server and diff it against the anchor text exactly; don't assume the anchor that worked in a previous package version still matches, especially for blocks that get touched by more than one mod over time (see ยง7) |
| A patch appears to have installed successfully (no warning, shows in Package Manager's installed list) but the change isn't visible on the live page | Check the raw file on the server directly (not the rendered browser page) to isolate whether the source was actually patched. If the source was patched correctly but the page still shows the old version, suspect PHP OPcache or SMF's own settings/menu cache serving stale content โ clear both (PHP-FPM/Apache restart or opcache_reset(), plus SMF's Admin > Maintenance cache-clear) before concluding the patch itself failed |