Show Posts
|
Pages: [1] 2 3 »
|
I ran into a nice suggestion from a long time ago (~12 years): SMF breaks up long words by inserting a space every 79 characters (it is a space in a <span> with a negative margin). Example: here are 120 'a' characters: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa When copying/pasting the chars, the space is visible at the 80th position, which is very annoying... Instead, SMF should insert the standardized <wbr> tag ( word break opportunity) already recognized by most browsers. In theory <wbr> is identical to U+200B (ZERO-WIDTH SPACE) but this is false; for example the current Chrome version on Linux (Version 23.0.1271.97) replaces U+200B with '#' when copying & pasting to a non-UTF8 application, whereas <wbr> is nicely invisible... Maybe newer members haven't encountered this particular issue before, but I know exactly what the above poster is complaining about, and I'm sure that many other people do, too. Basically, if you (with some exceptions that aren't worth getting into) post an unbroken sequence of 80 or more letters, digits, underscores and/or periods, then it'll automatically get divided into chunks which are each 79 characters in length (or less, in the case of the final chunk). So, for example, if you were to post the SHA-512 of the text "How would you like to suck my balls, Mr. Garrison?", then instead of it appearing like this:  It would appear like this:  (See that tiny space at around the two-thirds point? Between dbcc and 9e06?) That "breaker" space will, among other things, cause problems with copy-pasting (because there's now a space in the content that the author didn't intend for there to be), and will also affect double-click selecting, like this:  So, what I've done is add a new kind of "breaker" to SMF (to go along with the three existing variations that are chosen between based on what browser the BBCode parser thinks it's producing markup for). This new breaker avoids the problems described above and is used by default (but, I've left the older breakers accessible by a context variable in case theymos wishes to selectively flip between the new and the old behavior). Here's the diff: --- baseline/Sources/Subs.php 2011-09-17 21:59:55.000000000 +0000 +++ modified/Sources/Subs.php 2025-02-16 23:39:26.000000000 +0000 @@ -1860,24 +1860,27 @@ if (!empty($modSettings['fixLongWords']) && $modSettings['fixLongWords'] > 5) { // This is SADLY and INCREDIBLY browser dependent. if ($context['browser']['is_gecko'] || $context['browser']['is_konqueror']) $breaker = '<span style="margin: 0 -0.5ex 0 0;"> </span>'; // Opera... elseif ($context['browser']['is_opera']) $breaker = '<span style="margin: 0 -0.65ex 0 -1px;"> </span>'; // Internet Explorer... else $breaker = '<span style="width: 0; margin: 0 -0.6ex 0 -1px;"> </span>'; + if ($context['bbc_use_modern_breaker'] ?? true) + $breaker = '<wbr />'; + // PCRE will not be happy if we don't give it a short. $modSettings['fixLongWords'] = (int) min(65535, $modSettings['fixLongWords']); // The idea is, find words xx long, and then replace them with xx + space + more. if (strlen($data) > $modSettings['fixLongWords']) { // This is done in a roundabout way because $breaker has "long words" :P. $data = strtr($data, array($breaker => '< >', ' ' => $context['utf8'] ? "\xC2\xA0" : "\xA0")); $data = preg_replace( '~(?<=[>;:!? ' . $non_breaking_space . '\]()]|^)([\w\.]{' . $modSettings['fixLongWords'] . ',})~e' . ($context['utf8'] ? 'u' : ''), "preg_replace('/(.{" . ($modSettings['fixLongWords'] - 1) . '})/' . ($context['utf8'] ? 'u' : '') . "', '\\\$1< >', '\$1')", $data);
When doing these sorts of fixes, I always hem and haw on whether or not it makes sense to keep using the older behavior on old posts, and only use the newer behavior on new posts... I guess, I don't much like the idea of posts changing in ways that the author didn't account for (for example, I hate that one of my own posts was mangled by the wordfilter at some point after I authored it). I (mostly) lean toward wanting to keep old posts displaying as they did at the time they were authored. For example, in the post I quoted from above, mrb is demonstrating the problem he's describing, as in, if you try to copy-paste his example-sequence, then you'll get the result he's talking about: 79 characters, followed by a space, followed by 41 characters. But, after this fix is applied, if you then tried to copy-paste mrb's example, you'll find that you get 120 contiguous characters, so it'll look (from the perspective of someone reading that post cold) like mrb must have been confused or mistaken when he constructed that example. I dunno, maybe I'm just overthinking things, but, in case theymos feels that the old behavior is worth preserving, here are two additional diffs that will make sure (at least, in the two places that are important, I think) that old posts won't be affected by this fix: --- baseline/Sources/Display.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/Display.php 2025-02-17 01:11:58.000000000 +0000 @@ -878,24 +878,26 @@ else { $memberContext[$message['ID_MEMBER']]['can_view_profile'] = allowedTo('profile_view_any') || ($message['ID_MEMBER'] == $ID_MEMBER && allowedTo('profile_view_own')); $memberContext[$message['ID_MEMBER']]['is_topic_starter'] = $message['ID_MEMBER'] == $context['topic_starter_id']; } $memberContext[$message['ID_MEMBER']]['ip'] = $message['posterIP']; // Do the censor thang. censorText($message['body']); censorText($message['subject']); + $context['bbc_use_modern_breaker'] = (int)$message['ID_MSG'] >= 65073000; + // Run BBC interpreter on the message. $message['body'] = parse_bbc($message['body'], $message['smileysEnabled'], $message['ID_MSG']); // Compose the memory eat- I mean message array. $output = array( 'attachment' => loadAttachmentContext($message['ID_MSG']), 'alternate' => $counter % 2, 'id' => $message['ID_MSG'], 'href' => $scripturl . '?topic=' . $topic . '.msg' . $message['ID_MSG'] . '#msg' . $message['ID_MSG'], 'link' => '<a href="' . $scripturl . '?topic=' . $topic . '.msg' . $message['ID_MSG'] . '#msg' . $message['ID_MSG'] . '">' . $message['subject'] . '</a>', 'member' => &$memberContext[$message['ID_MEMBER']], 'icon' => $message['icon'], --- baseline/Sources/Profile.php 2013-10-21 19:01:11.000000000 +0000 +++ modified/Sources/Profile.php 2025-02-17 01:12:02.000000000 +0000 @@ -1479,24 +1479,26 @@ } // Start counting at the number of the first message displayed. $counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start']; $context['posts'] = array(); $board_ids = array('own' => array(), 'any' => array()); while ($row = mysql_fetch_assoc($request)) { // Censor.... censorText($row['body']); censorText($row['subject']); + $context['bbc_use_modern_breaker'] = (int)$row['ID_MSG'] >= 65073000; + // Do the code. $row['body'] = parse_bbc($row['body'], $row['smileysEnabled'], $row['ID_MSG']); // And the array... $context['posts'][$counter += $reverse ? -1 : 1] = array( 'body' => $row['body'], 'counter' => $counter, 'category' => array( 'name' => $row['cname'], 'id' => $row['ID_CAT'] ),
|
|
|
I mentioned this one at the end of December: There are requests for a "spoiler" tag going all the way back to 2011. I started work on this a few months ago, and am now finished with it. I experimented with a lot of different approaches, but the one I eventually settled on is just a simple button that replaces itself (once you click/tap on it) with whatever content you'd like. I've named the tag [hide] rather than [spoiler]. Basically, you can wrap anything you'd like in [hide]...[/hide] tags, and then rather than the content itself appearing, instead you just get this neat little "Show" button:  (That's obviously just an example image, so clicking on it won't do anything.) Something that works particularly well with this approach is making quotes more compact: (Seriously, stop clicking things.)  I can see this being a nice way (for the people who choose to start using it) to post more courteously, and to save others from having to scroll through huge images and big tables and whatnot. I think I've squared away every concern I've run into. For example, I worried about how this tag might negatively affect SEO, but worked around that by making the content rather than the button appear for guests (on the basis that that's the most reliable way I can think of to make sure that hidden content won't confuse web crawlers). I've also made it so that you can tack a ;unhide on the end of URLs, so that there's some way for people who are both logged-in and refuse to enable JavaScript to still be able to view everything (long term, I really can't see always trying to accommodate the no-JS crowd, but if it turns out to be a genuine sticking point, then I'll add a profile-setting that does the same thing as ;unhide, though I'd really prefer not to, because there are various things that I'd like to add to Bitcointalk that just have no way of working well without client-side scripting, so one or two final concessions to the no-JS peeps would be pretty pointless, all things considered). And though I expect very few people use them, I've also made sure that the tag recursively auto-expands for the wap/ wap2/ imode and action=printpage features. I'll post the patch sometime in January, but, in the meantime, if anyone has any concerns they'd like to raise, or believes that I haven't considered something deeply enough, then I'd be grateful for your thoughts. Most of the above description still stands, but I've since done more reading/thinking and changed my mind about the SEO impact. So, instead of the feature working one way for guests and another way for non-guests, it now just works the same way for everyone. Something that the above description skipped over is that it's actually a pair of tags that'll be added: a block-level tag called [hide], and an inline-level tag called [hide-inline]. (Don't worry too much about the difference, the block-level [hide] tag is almost always the one you want, with the [hide-inline] tag only really being useful in what I expect will be rare situations.) Another thing that the above description didn't mention is that there's a new button added for the [hide] tag (next to the "Insert List" button, located at the bottom-right):  Here are the diffs for @theymos (in the order I recommend applying them): --- baseline/Themes/default/style.css 2007-12-20 19:44:06.000000000 +0000 +++ modified/Themes/default/style.css 2025-01-08 02:06:35.000000000 +0000 @@ -451 +451,9 @@ } + +/* The "Show" button for the 'hide' and 'hide-inline' tags. */ +button.show_hidden_content +{ + color: #476c8e; + background-color: #f1f2f4; + border: 1px solid #d0d0e0; +} (The above diff makes sense for my version of SMF, but in theymos' version the file that should be adjusted is inside the Themes/custom1 directory.) --- baseline/Sources/Subs.php 2011-09-17 21:59:55.000000000 +0000 +++ modified/Sources/Subs.php 2025-01-08 02:16:41.000000000 +0000 @@ -1271,24 +1271,35 @@ 'tag' => 'green', 'before' => '<span style="color: green;">', 'after' => '</span>', ), array( 'tag' => 'glow', 'type' => 'unparsed_commas', 'test' => '[#0-9a-zA-Z\-]{3,12},([012]\d{1,2}|\d{1,2})(,[^]]+)?\]', 'before' => $context['browser']['is_ie'] ? '<table border="0" cellpadding="0" cellspacing="0" style="display: inline; vertical-align: middle; font: inherit;"><tr><td style="filter: Glow(color=$1, strength=$2); font: inherit;">' : '<span style="background-color: $1;">', 'after' => $context['browser']['is_ie'] ? '</td></tr></table> ' : '</span>', ), + array( + 'tag' => 'hide', + 'before' => isset($_GET['unhide']) || WIRELESS ? '<div>' : '<button type="button" class="show_hidden_content" onclick="this.style.display = \'none\'; this.nextElementSibling.style.display = \'\';" style="display: block;">Show</button><div style="display: none;">', + 'after' => '</div>', + 'block_level' => true, + ), + array( + 'tag' => 'hide-inline', + 'before' => isset($_GET['unhide']) || WIRELESS ? '<span>' : '<button type="button" class="show_hidden_content" onclick="this.style.display = \'none\'; this.nextElementSibling.style.display = \'\';">Show</button><span style="display: none;">', + 'after' => '</span>', + ), array( 'tag' => 'hr', 'type' => 'closed', 'content' => '<hr />', 'block_level' => true, ), array( 'tag' => 'html', 'type' => 'unparsed_content', 'content' => '$1', 'block_level' => true, 'disabled_content' => '$1', ), @@ -1705,24 +1716,28 @@ $disabled['me'] = true; // Color coding doesn't make sense. $disabled['php'] = true; // Links are useless on paper... just show the link. $disabled['ftp'] = true; $disabled['url'] = true; $disabled['iurl'] = true; $disabled['email'] = true; $disabled['flash'] = true; + // Can't click-to-reveal on paper... just show the content. + $disabled['hide'] = true; + $disabled['hide-inline'] = true; + // !!! Change maybe? if (!isset($_GET['images'])) $disabled['img'] = true; // !!! Interface/setting to add more? } $open_tags = array(); $message = strtr($message, array("\n" => '<br />')); // The non-breaking-space looks a bit different each time. $non_breaking_space = $context['utf8'] ? ($context['server']['complex_preg_chars'] ? '\x{C2A0}' : chr(0xC2) . chr(0xA0)) : '\xA0'; (This diff is where most of the feature exists.) python3 -c "open('hide.gif', 'wb').write(b'GIF89a\x17\x00\x16\x00\xa1\x03\x00\x4f\x4f\x4f\x35\x5d\x80\x86\xa4\xbe\x00\x00\x00\x21\xf9\x04\x01\x0a\x00\x03\x00\x2c\x00\x00\x00\x00\x17\x00\x16\x00\x00\x02\x36\x9c\x8f\xa9\xcb\xed\x0f\xa3\x9c\x62\xb2\x6a\x55\x05\x9c\x5b\x20\x00\x47\x17\x79\x55\x30\x8a\x8e\x67\xa0\xa8\xc1\x32\x71\x40\xa7\xcf\x3c\x6c\xea\x1d\x83\x7b\xd9\x11\x61\x32\x88\x21\xd1\x60\x3c\x2a\x97\xcc\xe6\xa3\x00\x00\x3b')" (The above isn't a diff, but rather a command that can be executed to re-create the icon I made with Inkscape and GIMP. The 101-byte hide.gif file that the above command creates should be moved into the Themes/custom1/images/bbc directory.) --- baseline/Themes/default/Post.template.php 2008-04-30 18:30:34.000000000 +0000 +++ modified/Themes/default/Post.template.php 2025-01-08 02:28:46.000000000 +0000 @@ -783,24 +783,25 @@ 'table' => array('code' => 'table', 'before' => '[table]', 'after' => '[/table]', 'description' => $txt[436]), 'tr' => array('code' => 'td', 'before' => '[tr]', 'after' => '[/tr]', 'description' => $txt[449]), 'td' => array('code' => 'td', 'before' => '[td]', 'after' => '[/td]', 'description' => $txt[437]), array(), 'sup' => array('code' => 'sup', 'before' => '[sup]', 'after' => '[/sup]', 'description' => $txt[447]), 'sub' => array('code' => 'sub', 'before' => '[sub]', 'after' => '[/sub]', 'description' => $txt[448]), 'tele' => array('code' => 'tt', 'before' => '[tt]', 'after' => '[/tt]', 'description' => $txt[440]), array(), 'code' => array('code' => 'code', 'before' => '[code]', 'after' => '[/code]', 'description' => $txt[259]), 'quote' => array('code' => 'quote', 'before' => '[quote]', 'after' => '[/quote]', 'description' => $txt[260]), array(), 'list' => array('code' => 'list', 'before' => '[list]\n[li]', 'after' => '[/li]\n[li][/li]\n[/list]', 'description' => $txt[261]), + 'hide' => array('code' => 'hide', 'before' => '[hide]', 'after' => '[/hide]', 'description' => 'Hide Content'), ); $found_button = false; // Here loop through the array, printing the images/rows/separators! foreach ($context['bbc_tags'][0] as $image => $tag) { // Is there a "before" part for this bbc button? If not, it can't be a button!! if (isset($tag['before'])) { // Is this tag disabled? if (!empty($context['disabled_tags'][$tag['code']])) continue; (This diff adds the new "Hide Content" button to the BBCode editor.) I considered including diffs to update the help section on posting and the BBCode reference, but those diffs are a bit messy (and bigger than the above diffs combined). Perhaps I'll update that in some later patch (and maybe add the other tags that are missing from it, too).
(I often try to make these "patch" topics substantive beyond the code, and pitch everyone on the usefulness of a thing, and maybe explain some of the thinking and trial-and-error that went into it. But, I've been struggling to stay enthusiastic about the forum lately, and though I expect I'll bounce back and feel positive about the forum once again, for now I'm in kind of a matter-of-fact/humorless mood. I'm sorry about that. If there's some technical detail that interests you, or you'd like to know why I did or didn't do something in a certain way, send me a PM. It's time-wise very expensive for me to recall/unpack all of my thinking on something and to respond properly to questions/suggestions, and I often leave posts unanswered because I either can’t find the time to reply to them, or because I’ve forgotten about them by the time that I do have surplus bandwidth. If you send me a question that you've clearly put some thought into, I promise to at some point get back to you with an answer.)
|
|
|
Back in May I remember seeing a suggestion that moderators should leave some kind of an explanation post whenever they lock a topic... I can't imagine that many mods didn't think:  I remember a part of that suggestion jumping out at me, though: But for locking a topic there's nothing to signify it was a moderator action.
At the time, I remember quickly skimming over the relevant bits of SMF's code and coming away with the (wrong) conclusion that it would be a bit too complicated to justify doing, so I stopped thinking about it. Recently, while working on something else, I bumped into the following code-comment: // A moderator-lock (1) can override a user-lock (2). Seeing those integer values jogged my memory about this, and I couldn't quite remember why I had previously decided that exposing this information to the user would be difficult... I guess I was on decaf, or something, because taking a fresh look at things, it's actually really simple.  So, I put together a largish patch (much bigger than the one I finally arrived at below) that adds a tooltip and makes the padlock icon (in all of the locations within SMF that it appears) a different color depending on the lock: blue when it's a user-lock, and copper/brown when it's a moderator-lock. But, playing around with that in my test environment, I couldn't shake the feeling that, while it does look kind of cool, it's just not important-enough information to justify that kind of presentation/complexity: doing it all in a polished way, and one that wouldn't create any new problems/inconsistencies, leads to a fairly big/messy patch. Reminding myself that this is information that nobody really needs to have at their fingertips, and it's just something that would be nice to be able to get to in some way, I simplified the whole thing down to a 1-line change that exposes the information as a tooltip on that little pre-composited icon that appears at the top-left of topic pages:   Here's the diff for @theymos: --- baseline/Themes/default/Display.template.php 2010-10-22 01:38:35.000000000 +0000 +++ modified/Themes/default/Display.template.php 2024-10-31 06:53:43.827818859 +0000 @@ -202,7 +202,7 @@ <table width="100%" cellpadding="3" cellspacing="0" border="0" class="tborder" style="border-bottom: 0;"> <tr class="catbg3"> <td valign="middle" width="2%" style="padding-left: 6px;"> - <img src="', $settings['images_url'], '/topic/', $context['class'], '.gif" align="bottom" alt="" /> + <img src="', $settings['images_url'], '/topic/', $context['class'], '.gif" align="bottom" alt=""', (empty($context['is_locked']) ? '' : ($context['is_locked'] == '2' ? ' title="Locked by the topic-starter"' : ' title="Locked by a moderator"')), ' /> </td> <td width="13%"> ', $txt[29], '</td> <td valign="middle" width="85%" style="padding-left: 6px;" id="top_subject">
(The descriptions I settled on do make good sense, I think, but it's worth pointing out that they're not technically ideal. For example, if someone with moderation privileges locks their own topic, then you might expect it to read "Locked by the topic-starter" because, well, they are the topic-starter in that case, but, behind the scenes, the lock is recorded in the database as a moderator-lock, not a user-lock, so it'll read "Locked by a moderator". I guess, if you wanted perfectly symmetrical descriptions, then you could make them "Locked by a moderator" and "Locked by a non-moderator", or something, but that exchanges clarity for correctness in a way that doesn't really work, IMO. Anyway, I'll leave the language-finessing up to theymos.)
|
|
|
I did a similar patch a while ago to add a "Copy" button to [code] blocks. I figured that it would get accepted pretty quickly, but I'm glad that it wasn't, because I think that this new patch is a bit nicer. The old patch had two problems with it (that I can think of): (1) Programmatic access to the clipboard is something that I expect to get more and more fragile over time. There's already some not-completely-reliable browser logic that stops the clipboard from being interacted with in non-user-initiated contexts. So, rather than implement something that I expect will pick up more and more cross-browser weirdness as time goes on, I think it's better to solve basically the same problem (how finicky it is to copy-paste [code], especially on mobile), but with something that's likely to behave more reliably across more browsers. (2) The previous patch added the button whether JavaScript was enabled or not. This patch only adds the button when JavaScript is available. Not a big deal, but, it is kind of goofy for the button to be there if it's not going to do anything when you click on it. Better to just not even show it in those situations. Compared to the version that people helped me test, the only user-facing change I've made is to tweak the opacity (so that it's distinct from the text it's adjacent to, and so that it's a little softer/quieter in appearance):  (That's an image, above. You tried to use that select link/button, didn't you?)  No diff(s) this time... I'm experimenting with a new way to do certain kinds of patches, and this is one of those patches: I've sent the code to theymos.
|
|
|
This is just a really small improvement that I remember Loyce asking for (a while back). Basically, when you're looking at someone's trust page, there's sometimes no easy way to ascend/get-back to that user's profile page. I occasionally bump into this one myself (for example, when I click on a link that lands me on some user's trust summary, and I then want to examine that user's post history, or something). So, this patch changes the "Trust summary for ..." header to include a link back to that user's profile page, like this:  (For consistency, I've made that same change to the merit summary page, too.) No diff(s) this time... I'm experimenting with a new way to do certain kinds of patches, and this is one of those patches: I've sent the code to theymos.
|
|
|
Hey, everybody!  A while ago, I did a patch to add a "Copy" button to [code] blocks. Thinking about it a little more, I think a "[Select]" link (like the one I've seen in later SMF versions) might actually make more sense... After coming up with my own implementation of this, I took a peek at what the SMF devs did for 2.x and noticed that they're special-casing things for different browsers. I'd like to know if that special-casing is still necessary these days, so I'd appreciate some help with testing this on different browsers. Basically, go to any forum page that contains [code] blocks (like the page you're currently on), and then open up the developer console in your browser and paste/run the following code: function addSelectionLinksToCodeBlocks() {
const allCodeDivs = document.querySelectorAll("div.codeheader + div.code");
for (const codeDiv of allCodeDivs) {
const codeHeaderDiv = codeDiv.previousElementSibling;
if (!codeHeaderDiv.querySelector("a.selectionLink")) {
const selectionLink = document.createElement("a");
selectionLink.onclick = () => {
const range = document.createRange();
const selection = document.getSelection();
range.selectNodeContents(codeDiv);
selection.removeAllRanges();
selection.addRange(range);
return false; };
selectionLink.className = "selectionLink";
selectionLink.href = "javascript:void(0);";
selectionLink.textContent = "[Select]";
codeHeaderDiv.appendChild(document.createTextNode(" "));
codeHeaderDiv.appendChild(selectionLink); } } }
addSelectionLinksToCodeBlocks(); (Make sure to copy it in its entirety and to hit enter after pasting it.) You should now see that each [code] block has a little "[Select]" link at the top. Click on that link and check if the code for that block gets correctly selected or not. If it doesn't work for you, then please post your browser details (just the name and version will do). If you have thoughts about preferring the "[Select]" link to the "Copy" button (or vice versa), feel free to leave those, too. Thanks. (I'm not really looking for code critiques. If you're a programmer and something looks odd to you, probably I'm either sticking to some established SMF 1.x idiom, or it's because I had to write this code in a particular way to get Cloudflare's WAF to stop blocking me from posting it.)
|
|
|
So, there's this really annoying and long-standing forum bug that "squashes" the first column in tables (but only after you've posted; it doesn't present during preview). For example, here's a table from one of theymos' old posts, as it would appear if not for the presence of this bug:  And here's how that table actually displays:  The earliest mention of this bug (that I could find) is from 2014: the table above is shown correctly in the previewwindow for the post, but after posting the spacing of first column is squashed.
In that same topic, Foxpup pointed out this hinky bit of CSS: /* prevent gap between post icon and subject */ div#bodyarea table.bordercolor td td td.td_headerandpost td:first-child { width: 26px; }
I don't know who added that CSS, or when it was added, but here's what I think happened: (*) There's a flaw in original SMF 1.1.19 that makes a variable-length gap appear between a post's icon and its subject (beyond the comment in the CSS, I know this to be the case because that same flaw is present in my own branch of SMF, which is also based on 1.1.19). (*) Someone attempted repair of that flaw by coming up with a CSS selector to (try to) isolate just the <td>s that contain each rendered post's icon, so that they could fix the width of those <td>s to 26px (which does succeed in repairing the "gap" flaw). (*) The problem is that the selector they came up with has a flaw of its own: it selects more than just the <td>s with post icons in them, reaching into the post itself and affecting first-child <td>s there, too (it does this, by accident, because of an apparent misunderstanding of descendant selectors: because descendants are a less specific concept than children, there's actually nothing in that selector that prevents it from reaching deeper into the post, affecting <td>s that it was never meant to affect). I've come up with three ways to fix this bug (I mean, there are many ways, with various pros and cons, but the below three are the ones that make the most sense to me): Fix AThis just fixes the CSS selector so that it does what the original author clearly intended. I can see an argument for simplifying the selector around a new class added to the relevant <td>s, but, there's also something to be said for just leaving everything the way it is (except for one tiny little selector tweak, that is). --- baseline/Themes/custom1/style.css 2024-08-27 09:53:04.000000000 +0000 +++ modified/Themes/custom1/style.css 2024-08-27 23:11:37.000000000 +0000 @@ -551,7 +551,7 @@ } /* prevent gap between post icon and subject */ -div#bodyarea table.bordercolor td td td.td_headerandpost td:first-child +div#bodyarea table.bordercolor td td td.td_headerandpost > table td:first-child { width: 26px; }
Fix BThis removes the buggy CSS entirely, and then fixes the now-exposed "gap" flaw in a less brittle way. I mean, I get that putting a width attribute on the <td> probably gives modern web devs the heebie-jeebies, but, there's plenty of precedent for that in SMF's ancient markup, and there's no real value in having a very small handful of spots in the markup/styling that try to do things "the right way". I think era-appropriate fixes are fine for SMF, and that any serious attempt to modernize the frontend should be done independently of the legacy frontend. --- baseline/Themes/custom1/style.css 2024-08-27 09:53:04.000000000 +0000 +++ modified/Themes/custom1/style.css 2024-08-27 23:24:18.000000000 +0000 @@ -553,6 +552,0 @@ -/* prevent gap between post icon and subject */ -div#bodyarea table.bordercolor td td td.td_headerandpost td:first-child -{ - width: 26px; -} -
--- baseline/Themes/default/Display.template.php 2010-10-22 01:38:35.000000000 +0000 +++ modified/Themes/default/Display.template.php 2024-08-27 23:32:38.000000000 +0000 @@ -372,9 +372,9 @@ </div> </td> <td valign="top" width="85%" height="100%"> <table width="100%" border="0"><tr> - <td valign="middle"><a href="', $message['href'], '"><img src="', $message['icon_url'] . '" alt="" border="0" /></a></td> + <td valign="middle" width="26"><a href="', $message['href'], '"><img src="', $message['icon_url'] . '" alt="" border="0" /></a></td> <td valign="middle"> <div style="font-weight: bold;" id="subject_', $message['id'], '"> <a href="', $message['href'], '">', $message['subject'], '</a> </div>';
Fix CThe problem (as I see it) with either of the previous fixes is that they repair the bug for all posts (old and new). I guess most people won't really see eye-to-eye with me on this one, but, I'd prefer it if new posts weren't affected by this bug (obviously) but that older posts were. My thinking is that people have for 10+ years been using all sorts of workarounds and tricks to avoid this bug, and those workaround-remnants are inevitably going to display slightly differently post-fix. From a history-preservation perspective, I like the idea that posts that were constructed in the presence of this bug will not appearance-wise be affected by the fix. --- baseline/Themes/custom1/style.css 2024-08-27 09:53:04.000000000 +0000 +++ modified/Themes/custom1/style.css 2024-08-27 23:44:01.000000000 +0000 @@ -550,8 +550,8 @@ display: none; } -/* prevent gap between post icon and subject */ -div#bodyarea table.bordercolor td td td.td_headerandpost td:first-child +/* This is an adaptation of an older, buggy CSS selector. It's being kept so that certain posts will display in a bug-for-bug compatible way. */ +div#bodyarea table.bordercolor td td td.td_headerandpost.with_column_bug td:first-child { width: 26px; }
--- baseline/Themes/default/Display.template.php 2010-10-22 01:38:35.000000000 +0000 +++ modified/Themes/default/Display.template.php 2024-08-27 23:55:37.000000000 +0000 @@ -372,7 +372,7 @@ </div> </td> - <td valign="top" width="85%" height="100%"> + <td valign="top" width="85%" height="100%" class="td_headerandpost', $message['id'] < 64471000 ? ' with_column_bug' : '', '"> <table width="100%" border="0"><tr> - <td valign="middle"><a href="', $message['href'], '"><img src="', $message['icon_url'] . '" alt="" border="0" /></a></td> + <td valign="middle" width="26"><a href="', $message['href'], '"><img src="', $message['icon_url'] . '" alt="" border="0" /></a></td> <td valign="middle"> <div style="font-weight: bold;" id="subject_', $message['id'], '">
@theymos: That last diff is a little confusing because class="td_headerandpost" is something that's already present in your branch. The relevant change is just to make sure that the icon-containing <td> has a width="26" (same as with Fix B), and that the td_headerandpost <td> comes out either with two classes ( class="td_headerandpost with_column_bug") or one ( class="td_headerandpost") depending on some predicate that separates old posts from new. If you can pinpoint when this bug was introduced, then you might also consider changing the predicate to account for that (e.g. $message['id'] > 8800000 && $message['id'] < 64471000), so that posts that displayed correctly before this bug, will return to displaying correctly. (Obviously, the message IDs I've used are just examples that need to be replaced with more precise ones.)
|
|
|
This one was suggested by dkbit98. (Thanks for kicking off the suggestions thread with an easy one; being able to quickly fix the first thing suggested makes me look badass, yo.)  The area at the very top of most pages looks like this:  That area can be "collapsed" (by clicking on the minus icon next to the date/time) to save some vertical space:  The problem is that the setting isn't being saved/persisted, so you have to keep collapsing it again and again as you navigate the site. What's interesting is that it is being persisted when you're not logged-in... That's weird, right? Why does it only remember to stay collapsed when you're not logged-in? It turns out that there are two ways in which this setting gets saved: When you're logged-in, it gets saved as a so-called "theme option" via a JavaScript function named smf_setThemeOption that depends on a support endpoint ( /index.php?action=jsoption), and when you're not logged-in, it gets saved as a cookie. (At least, that's how it's supposed to work.) One of those two ways is still working (the cookie way), and the other is currently broken (the theme option way). I can only hazard a guess as to why smf_setThemeOption() no longer works, but I think what probably happened is that theymos disabled the support endpoint for it. Looking at the code for that endpoint, I can see why he might have done that (it's not a well-designed feature; it exposes too much attack surface relative to its usefulness). So, I can see two ways to fix the problem: Fix ASince the feature still works as intended by using cookies when you're a guest, this fix just makes it so that the feature always relies on cookies (whether you're logged-in or not). The advantage to fixing it this way is that the dodgy endpoint can stay disabled. The disadvantage is that the state is only persisted in a cookie, so if you sign in from a different device, or if you clear your cookies, then it'll "forget" (not a big deal, IMO). --- baseline/Themes/default/index.template.php 2008-04-30 18:30:34.000000000 +0000 +++ modified/Themes/default/index.template.php 2024-07-18 02:47:24.000000000 +0000 @@ -45,24 +45,27 @@ /* The version this template/theme is for. This should probably be the version of SMF it was created for. */ $settings['theme_version'] = '1.1'; /* Set a setting that tells the theme that it can render the tabs. */ $settings['use_tabs'] = true; /* Use plain buttons - as oppossed to text buttons? */ $settings['use_buttons'] = true; /* Show sticky and lock status seperate from topic icons? */ $settings['seperate_sticky_lock'] = true; + + /* Always use cookies to shrink/expand the headers? (Rather than cookies when not logged-in, and theme options otherwise.) */ + $settings['header_collapse_by_cookie'] = true; } // The main sub template above the content. function template_main_above() { global $context, $settings, $options, $scripturl, $txt, $modSettings; // Show right to left and the character set for ease of translating. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"', $context['right_to_left'] ? ' dir="rtl"' : '', '><head> <meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '" /> <meta name="description" content="', $context['page_title'], '" />', empty($context['robot_no_index']) ? '' : ' @@ -105,66 +108,66 @@ // If we're viewing a topic, these should be the previous and next topics, respectively. if (!empty($context['current_topic'])) echo ' <link rel="prev" href="', $scripturl, '?topic=', $context['current_topic'], '.0;prev_next=prev" /> <link rel="next" href="', $scripturl, '?topic=', $context['current_topic'], '.0;prev_next=next" />'; // If we're in a board, or a topic for that matter, the index will be the board's index. if (!empty($context['current_board'])) echo ' <link rel="index" href="' . $scripturl . '?board=' . $context['current_board'] . '.0" />'; // We'll have to use the cookie to remember the header... - if ($context['user']['is_guest']) + if ($context['user']['is_guest'] || $settings['header_collapse_by_cookie']) { $options['collapse_header'] = !empty($_COOKIE['upshrink']); $options['collapse_header_ic'] = !empty($_COOKIE['upshrinkIC']); } // Output any remaining HTML headers. (from mods, maybe?) echo $context['html_headers'], ' <script language="JavaScript" type="text/javascript"><!-- // --><![CDATA[ var current_header = ', empty($options['collapse_header']) ? 'false' : 'true', '; function shrinkHeader(mode) {'; // Guests don't have theme options!! - if ($context['user']['is_guest']) + if ($context['user']['is_guest'] || $settings['header_collapse_by_cookie']) echo ' document.cookie = "upshrink=" + (mode ? 1 : 0);'; else echo ' smf_setThemeOption("collapse_header", mode ? 1 : 0, null, "', $context['session_id'], '");'; echo ' document.getElementById("upshrink").src = smf_images_url + (mode ? "/upshrink2.gif" : "/upshrink.gif"); document.getElementById("upshrinkHeader").style.display = mode ? "none" : ""; document.getElementById("upshrinkHeader2").style.display = mode ? "none" : ""; current_header = mode; } // ]]></script>'; // the routine for the info center upshrink echo ' <script language="JavaScript" type="text/javascript"><!-- // --><![CDATA[ var current_header_ic = ', empty($options['collapse_header_ic']) ? 'false' : 'true', '; function shrinkHeaderIC(mode) {'; - if ($context['user']['is_guest']) + if ($context['user']['is_guest'] || $settings['header_collapse_by_cookie']) echo ' document.cookie = "upshrinkIC=" + (mode ? 1 : 0);'; else echo ' smf_setThemeOption("collapse_header_ic", mode ? 1 : 0, null, "', $context['session_id'], '");'; echo ' document.getElementById("upshrink_ic").src = smf_images_url + (mode ? "/expand.gif" : "/collapse.gif"); document.getElementById("upshrinkHeaderIC").style.display = mode ? "none" : ""; current_header_ic = mode;
Fix BThe other way to go would be to resurrect the disabled endpoint... The advantage to fixing it this way is that the state would be properly persisted and would effectively be "remembered" no matter what. There's also a very small traffic-wise advantage to not using cookies for this. The disadvantage is that the forum's attack surface would increase (with the below repair: negligibly, in my estimation, but still a strict increase). I've hardened the offending endpoint, and the result looks safe to me, but theymos should apply his mind carefully to the if-guard I've added to SetJavaScript() before deciding whether it's sensible to restore the endpoint or not (especially considering that there's an alternative fix). --- baseline/Sources/Themes.php 2013-01-18 15:35:17.000000000 +0000 +++ modified/Sources/Themes.php 2024-07-18 02:50:57.000000000 +0000 @@ -1181,24 +1181,28 @@ $context['sub_template'] = $settings['catch_action']['sub_template']; } // Set an option via javascript. function SetJavaScript() { global $db_prefix, $ID_MEMBER, $settings, $user_info; // Sorry, guests can't do this. if ($user_info['is_guest']) obExit(false); + // Best to enforce the low-consequence subset of functionality that this function actually/currently gets called for... + if (empty($_GET['var']) || !in_array($_GET['var'], ['collapse_header', 'collapse_header_ic']) || !isset($_GET['val']) || !in_array($_GET['val'], ['0', '1']) || isset($_GET['th']) || isset($_GET['id'])) + obExit(false); + // Check the session id. checkSession('get'); // This good-for-nothing pixel is being used to keep the session alive. if (empty($_GET['var']) || !isset($_GET['val'])) redirectexit($settings['images_url'] . '/blank.gif'); $reservedVars = array( 'actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url',
(Though I've described the problem only with respect to the collapsible section at the top of the page, there's another one, the so-called "info center" at the bottom of the main page, that these fixes also address.)
|
|
|
I'm basically always on the lookout for little things to work on...
Especially when I'm taking a break from something bigger (for example, right now I'm working on a way to quickly filter PMs by sender/recipient), I like to stay productive by switching my focus to something else. It's particularly helpful to me (and my energy levels) when I have a bunch of (relatively) easy-to-complete things to pick from. Basically, I work on-and-off on the bigger stuff (which I'm not in short supply of), but I really enjoy bite-sized problems in between.
So, if you can think of something fairly specific that you'd like to see fixed/improved and you estimate it to be a small(ish) task (so, not something vague and big like: "Can you fix the forum's search?", or "Can you improve the forum's mobile-friendliness?", etc.), then please post it in this thread.
I probably won't participate all that much in this topic, but I promise to carefully read/consider every post. If I leave merit on your post (it won't be much, because I'm not a source), it likely means that I've added your suggestion to my backlog and that I intend to work on it at some point. If you see a suggestion in this thread that resonates with you, please leave some merit for that poster; if I notice a suggestion that's attracted a lot of merit, I'll probably add it to my backlog.
Thanks.
|
|
|
I've been working on this one for a while now. It was most recently raised by garlonicon, but I'm guessing that this issue has been discussed a great many times over the years. This is something that's handled neatly by the BPIP extension, but there are a lot of people (me included) that prefer not to install extensions, so something built-in would be nice... I was very tempted to take the same (clever) approach that the BPIP extension took, and build things around the endpoint that SMF's insert-quote feature uses ( /index.php?action=quotefast), but, after weighing the pros and cons, I ended up settling on an approach that doesn't introduce new UI elements, feels more SMFish (to me), and avoids depending on JavaScript. Basically, the approach I took began with a thought that I have to imagine more than a few people have had: Why not just make the existing "quote" button visible in locked threads? If you go poking around in SMF and make that button appear even in locked threads, you'll quickly discover that it doesn't actually work:  So, the next thing to do is to find a way to (safely) bypass that error: I did that by selectively tacking a ;peek onto the end of the quote button's URL, and then allowing such requests to avoid that lock-check. This turns out to be safe (in terms of not allowing everyone to actually post in locked threads) because (conveniently) there's another lock-check deeper in the code. Mission accomplished then, right? Well, kind of, but, if the concept is that you're only "peeking" at some post's BBCode, then the "Post reply" page that you land on could definitely use some adjustments: (*) There's no point in showing the post/preview buttons (in this context), because they'll only error-out if you try to use them (courtesy of that remaining, deeper lock-check). (*) There's no point in showing things like the old-topic warning, because, as before, you can't actually post anything. (*) The page title ("Post reply") is now misleading. (*) The "Topic Summary" at the bottom of the page is unnecessary. So, I took care of all of that by hiding the things that aren't relevant, and changing the page title to "Inspect message":  To help distinguish this different "kind" of quote button (compared to the kind that shows up in not-locked threads), I made the button render at 50% grayscale and added a tooltip to it:  Here are the diffs for @theymos: --- baseline/Themes/default/Display.template.php 2010-10-22 01:38:35.000000000 +0000 +++ modified/Themes/default/Display.template.php 2024-06-09 23:32:16.000000000 +0000 @@ -382,30 +382,35 @@ // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. echo ' <div class="smalltext">« <b>', !empty($message['counter']) ? $txt[146] . ' #' . $message['counter'] : '', ' ', $txt[30], ':</b> ', $message['time'], ' »</div></td> <td align="', !$context['right_to_left'] ? 'right' : 'left', '" valign="bottom" height="20" style="font-size: smaller;">'; // Can they reply? Have they turned on quick reply? if ($context['can_reply'] && !empty($options['display_quick_reply'])) echo ' <a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';num_replies=', $context['num_replies'], ';sesc=', $context['session_id'], '" onclick="doQuote(', $message['id'], ', \'', $context['session_id'], '\'); return false;">', $reply_button, '</a>'; // So... quick reply is off, but they *can* reply? elseif ($context['can_reply']) echo ' <a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';num_replies=', $context['num_replies'], ';sesc=', $context['session_id'], '">', $reply_button, '</a>'; + // So... they *can't* reply, but if they're logged-in then we should emit a button that at least lets them see/copy the BBCode if needed. + elseif (!$context['user']['is_guest']) + echo ' + <a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';num_replies=', $context['num_replies'], ';sesc=', $context['session_id'], ';peek" style="filter: grayscale(0.5);" title="Inspect message">', $reply_button, '</a>'; + // Can the user modify the contents of this post? if ($message['can_modify']) echo ' <a href="', $scripturl, '?action=post;msg=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';sesc=', $context['session_id'], '">', $modify_button, '</a>'; // How about... even... remove it entirely?! if ($message['can_remove']) echo ' <a href="', $scripturl, '?action=deletemsg;topic=', $context['current_topic'], '.', $context['start'], ';msg=', $message['id'], ';sesc=', $context['session_id'], '" onclick="return confirm(\'', $txt[154], '?\');">', $remove_button, '</a>'; // What about splitting it off the rest of the topic? if ($context['can_split']) echo ' <a href="', $scripturl, '?action=splittopics;topic=', $context['current_topic'], '.0;at=', $message['id'], '">', $split_button, '</a>';
--- baseline/Sources/Post.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/Post.php 2024-06-09 23:43:55.000000000 +0000 @@ -183,32 +183,36 @@ $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']); $context['notify'] = !empty($context['notify']); $context['sticky'] = !empty($_REQUEST['sticky']); } // !!! These won't work if you're posting an event! $context['can_notify'] = allowedTo('mark_any_notify'); $context['can_move'] = allowedTo('move_any'); $context['can_announce'] = allowedTo('announce_topic'); $context['locked'] = !empty($locked) || !empty($_REQUEST['lock']); // An array to hold all the attachments for this topic. $context['current_attachments'] = array(); + // Is this a "peek" request? (That is, one that's intended to only see/copy the BBCode of a post.) + $context['only_peeking'] = !isset($_GET['msg']) && !empty($_GET['quote']) && isset($_GET['peek']); + // Don't allow a post if it's locked and you aren't all powerful. - if ($locked && !allowedTo('moderate_board')) + // But make an exception for "peek" requests (and rely on the similar check in Post2() to pick up the slack). + if ($locked && !allowedTo('moderate_board') && !$context['only_peeking']) fatal_lang_error(90, false); // Check the users permissions - is the user allowed to add or post a poll? if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1') { // New topic, new poll. if (empty($topic)) isAllowedTo('poll_post'); // This is an old topic - but it is yours! Can you add to it? elseif ($ID_MEMBER == $ID_MEMBER_POSTER && !allowedTo('poll_add_any')) isAllowedTo('poll_add_own'); // If you're not the owner, can you add to any poll? else isAllowedTo('poll_add_any'); @@ -947,31 +951,31 @@ $context['error_type'] = 'minor'; } // What are you doing? Posting a poll, modifying, previewing, new post, or reply... if (isset($_REQUEST['poll'])) $context['page_title'] = $txt['smf20']; elseif ($context['make_event']) $context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar23'] : $txt['calendar20']; elseif (isset($_REQUEST['msg'])) $context['page_title'] = $txt[66]; elseif (isset($_REQUEST['subject'], $context['preview_subject'])) $context['page_title'] = $txt[507] . ' - ' . strip_tags($context['preview_subject']); elseif (empty($topic)) $context['page_title'] = $txt[33]; else - $context['page_title'] = $txt[25]; + $context['page_title'] = $context['only_peeking'] ? 'Inspect message' : $txt[25]; // Build the link tree. if (empty($topic)) $context['linktree'][] = array( 'name' => '<i>' . $txt[33] . '</i>' ); else $context['linktree'][] = array( 'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'], 'name' => $form_subject, 'extra_before' => '<span' . ($settings['linktree_inline'] ? ' class="smalltext"' : '') . '><b class="nav">' . $context['page_title'] . ' ( </b></span>', 'extra_after' => '<span' . ($settings['linktree_inline'] ? ' class="smalltext"' : '') . '><b class="nav"> )</b></span>' ); // If they've unchecked an attachment, they may still want to attach that many more files, but don't allow more than num_allowed_attachments. @@ -2027,30 +2031,33 @@ // Get the topic for display purposes. function getTopic() { global $topic, $db_prefix, $modSettings, $context; // Calculate the amount of new replies. $newReplies = empty($_REQUEST['num_replies']) || $context['num_replies'] <= $_REQUEST['num_replies'] ? 0 : $context['num_replies'] - $_REQUEST['num_replies']; if (isset($_REQUEST['xml'])) $limit = " LIMIT " . (empty($newReplies) ? '0' : $newReplies); else $limit = empty($modSettings['topicSummaryPosts']) ? '' : ' LIMIT ' . (int) $modSettings['topicSummaryPosts']; + // Previous posts won't be displayed by the template when $context['only_peeking'] is true, so save some cycles... + $limit = $context['only_peeking'] ? ' LIMIT 0' : $limit; + // If you're modifying, get only those posts before the current one. (otherwise get all.) $request = db_query(" SELECT IFNULL(mem.realName, m.posterName) AS posterName, m.posterTime, m.body, m.smileysEnabled, m.ID_MSG FROM {$db_prefix}messages AS m LEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = m.ID_MEMBER) WHERE m.ID_TOPIC = $topic" . (isset($_REQUEST['msg']) ? " AND m.ID_MSG < " . (int) $_REQUEST['msg'] : '') . " ORDER BY m.ID_MSG DESC$limit", __FILE__, __LINE__); $context['previous_posts'] = array(); while ($row = mysql_fetch_assoc($request)) { // Censor, BBC, ... censorText($row['body']); $row['body'] = parse_bbc($row['body'], $row['smileysEnabled'], $row['ID_MSG']);
--- baseline/Themes/default/Post.template.php 2008-04-30 18:30:34.000000000 +0000 +++ modified/Themes/default/Post.template.php 2024-06-09 23:53:13.000000000 +0000 @@ -278,45 +278,49 @@ echo ' <input type="hidden" name="eventid" value="', $context['event']['id'], '" />'; // Start the main table. echo ' <table border="0" width="100%" align="center" cellspacing="1" cellpadding="3" class="bordercolor"> <tr class="titlebg"> <td>', $context['page_title'], '</td> </tr> <tr> <td class="windowbg">', isset($context['current_topic']) ? ' <input type="hidden" name="topic" value="' . $context['current_topic'] . '" />' : '', ' <table border="0" cellpadding="3" width="100%">'; // If an error occurred, explain what happened. - echo ' + // Skip this if we're in the context of a "peek" request. (These errors/warnings aren't relevant in this context: the user can't post anything, that's why they're "peeking" in the first place.) + if (!$context['only_peeking']) + echo ' <tr', empty($context['post_error']['messages']) ? ' style="display: none"' : '', ' id="errors"> <td></td> <td align="left"> <div style="padding: 0px; font-weight: bold;', empty($context['error_type']) || $context['error_type'] != 'serious' ? ' display: none;' : '', '" id="error_serious"> ', $txt['error_while_submitting'], ' </div> <div style="color: red; margin: 1ex 0 2ex 3ex;" id="error_list"> ', empty($context['post_error']['messages']) ? '' : implode('<br />', $context['post_error']['messages']), ' </div> </td> </tr>'; // If it's locked, show a message to warn the replyer. - echo ' + // Skip this if we're in the context of a "peek" request. (This warning *seems* germane in this context, but it's not: the whole point of "peek" requests is to allow messages to be inspected from inside locked topics, so warning the user about something that they already know, and against an action that they're unable to take, is pretty silly.) + if (!$context['only_peeking']) + echo ' <tr', $context['locked'] ? '' : ' style="display: none"', ' id="lock_warning"> <td></td> <td align="left"> ', $txt['smf287'], ' </td> </tr>'; // Guests have to put in their name and email... if (isset($context['name']) && isset($context['email'])) { echo ' <tr> <td align="right" style="font-weight: bold;', isset($context['post_error']['long_name']) || isset($context['post_error']['no_name']) || isset($context['post_error']['bad_name']) ? 'color: red;' : '', '" id="caption_guestname"> ', $txt[68], ': </td> @@ -495,30 +499,43 @@ </tr> <tr> <td align="right"></td> <td class="smalltext"> <input type="radio" id="poll_hide" name="poll_hide" value="0"', $context['poll_options']['hide'] == 0 ? ' checked="checked"' : '', ' class="check" /> ', $txt['poll_options2'], '<br /> <input type="radio" id="poll_hide" name="poll_hide" value="1"', $context['poll_options']['hide'] == 1 ? ' checked="checked"' : '', ' class="check" /> ', $txt['poll_options3'], '<br /> <input type="radio" id="poll_hide" name="poll_hide" value="2"', $context['poll_options']['hide'] == 2 ? ' checked="checked"' : '', empty($context['poll_options']['expire']) ? ' disabled="disabled"' : '', ' class="check" /> ', $txt['poll_options4'], '<br /> <br /> </td> </tr>'; } // The below function prints the BBC, smileys and the message itself out. theme_postbox($context['message']); + // If we're in the context of a "peek" request, avoid emitting what follows past this point... just close off the markup and return. + if ($context['only_peeking']) + { + echo ' + </table> + </td> + </tr> + </table> + </form>'; + + return; + } + // If this message has been edited in the past - display when it was. if (isset($context['last_modified'])) echo ' <tr> <td valign="top" align="right"> <b>', $txt[211], ':</b> </td> <td> ', $context['last_modified'], ' </td> </tr>'; // If the admin has enabled the hiding of the additional options - show a link and image for it. if (!empty($settings['additional_options_collapsable'])) echo '
(There are one or two other bits of polish/functionality that I could have pursued, like preventing the template from emitting a few unnecessary background things, and adding the ability to "peek" directly from someone's post/topic history, but, the patch as it stands is kind of in a "sweet spot" complexity-wise, so I'm reluctant to push it further. Also, it's difficult to get theymos to come down from the mountain, so to speak, and when he does, I don't want him put off by a sprawling patch that touches too many files and makes too many changes.)
|
|
|
I've been thinking for a while now that rule #32 violations (unnecessary consecutive posts) are handled pretty poorly... As I understand it, when posts like that are reported and moderators merge them together, the author gets notified that one (or more) of their posts were deleted, but they're not given a specific reason why, so they're unlikely to learn anything from the interaction. I remember when I had an early post of mine deleted by a moderator, it was a pretty confusing/deflating experience, and I was kind of left guessing at what was wrong with my post... I think that especially when it's for something that amounts to a very minor posting misbehavior, being left to figure out on your own what you did wrong is even worse. In fact, in this case, I think overzealous reporting might actually do more harm than good (that is, it's probably counterproductive in terms of the forum's health: for minor violations, the benefit of having a superficially cleaner forum is probably more than matched by the damage caused in making this place feel inhospitable). Long-term, I'll make a patch to let moderators leave an optional reason for post deletion, which will hopefully make the general being-moderated experience less confusing/deflating (especially for newbies), but, for now, all I really have in mind are rule #32 violations (because I've been reporting quite a few of those lately, and I'm starting to feel like a douchecanoe for doing that, especially when you consider what I said in the previous paragraph). I think adding a warning against consecutive posts is a good thing to do (both to hopefully curb multi-posting, and to maybe reduce moderation busywork, along with providing users with at least some context for when they get notified that their posts are getting deleted/merged: after all, if they've been seeing and ignoring warnings given to them before they post, then they'll probably be able to correctly guess at what they've been doing wrong). So, this patch adds the following warning when you're about to add another post to a topic that you're already the latest, recent (<24 hours) poster in:  (There's even a handy dandy link that takes you directly to the appropriate "Edit message" page.)  Here are the diffs for @theymos: --- baseline/Sources/Post.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/Post.php 2024-05-12 23:43:54.000000000 +0000 @@ -1080,24 +1080,40 @@ )); if (!empty($topic)) getTopic(); $context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject'])); $context['show_additional_options'] = !empty($_POST['additional_options']) || !empty($_SESSION['temp_attachments']) || !empty($deleted_attachments); $context['is_new_topic'] = empty($topic); $context['is_new_post'] = !isset($_REQUEST['msg']); $context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $ID_FIRST_MSG); + // Add a warning against consecutive posts. This logic is placed where it is (near the end of this function) mostly because it expects getTopic() to have already been called (when applicable). + if (isset($context['previous_posts']) && count($context['previous_posts']) > 0) + { + $previous_post = $context['previous_posts'][0]; + $is_consecutive_post = $context['is_new_post'] && $previous_post['poster'] == $context['user']['name']; + $is_previous_post_recent = time() - $previous_post['timestamp'] < 86400; + $no_serious_error = empty($context['error_type']) || $context['error_type'] != 'serious'; + + if ($is_consecutive_post && $is_previous_post_recent && $no_serious_error) + { + $edit_post_url = $scripturl . '?action=post;msg=' . $previous_post['id'] . ';topic=' . $topic . '.0;sesc=' . $sc; + $context['post_error']['messages'][] = sprintf($txt['error_consecutive_post'], $edit_post_url); + $context['error_type'] = 'minor'; + } + } + // Register this form in the session variables. checkSubmitOnce('register'); // Finally, load the template. if (WIRELESS) $context['sub_template'] = WIRELESS_PROTOCOL . '_post'; elseif (!isset($_REQUEST['xml'])) loadTemplate('Post'); } function Post2() {
--- baseline/Themes/default/languages/Post.english.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Themes/default/languages/Post.english.php 2024-05-12 23:47:12.000000000 +0000 @@ -148,10 +148,11 @@ $txt['error_new_reply'] = 'Warning - while you were typing a new reply has been posted. You may wish to review your post.'; $txt['error_new_replies'] = 'Warning - while you were typing %d new replies have been posted. You may wish to review your post.'; $txt['error_new_reply_reading'] = 'Warning - while you were reading a new reply has been posted. You may wish to review your post.'; $txt['error_new_replies_reading'] = 'Warning - while you were reading %d new replies have been posted. You may wish to review your post.'; $txt['error_old_topic'] = 'Warning: this topic has not been posted in for at least ' . $modSettings['oldTopicDays'] . ' days.<br />Unless you\'re sure you want to reply, please consider starting a new topic.'; +$txt['error_consecutive_post'] = 'Warning: a recent post of yours is already the latest one in this topic.<br />Unless you\'re sure that a new post is necessary, please <a href="%s">add to your recent post</a> instead.'; // Use numeric entities in the below sixteen strings. $txt['notification_reply_subject'] = 'Topic reply: %s'; $txt['notification_reply'] = 'A reply has been posted to a topic you are watching by %s.' . "\n\n" . 'View the reply at: '; $txt['notification_sticky_subject'] = 'Topic stickied: %s';
|
|
|
Hey, everybody!  This suggestion came from Cyrus, and I think it's a really good idea, so I thought I'd sink some time into it, and put together a patch. Basically, the idea is to make it easier to identify the target post after clicking an ordinary post link, like: https://bitcointalk.org/index.php?topic=4193.msg60743#msg60743. Normally, the (optional) fragment identifier at the end of the URL ( #msg60743 in the example link) suffices to position the viewport so that the linked-to post is obvious/unambiguous. But, sometimes that mechanism isn't precise enough, like when the post is the last or second-to-last one on its page (then, I often find myself hovering over the subject link and comparing it to the address bar to confirm that I'm reading the right post). I also sometimes lose track of the linked-to post when I scroll around the page for a bit to get some context before reading the actual post (then, I find myself going to the address bar and hitting Enter so that the browser snaps to the right post again). In the worst case, both of those situations occur: After landing on the relevant topic-page, you scroll around a bit for some context, then you use the Enter trick to snap to the right post, but that's not reliable for the last or second-to-last post, so you have to do the address-comparing thing too.  I also have to imagine that report-handling would be a little more pleasant for moderators if they had a reliable way to always quickly identify the linked-to post. Anyway, it's tempting to make the linked-to post really stand out by putting a differently-colored background or border on it, or an inset/outset box-shadow or something, but, all the variations of that idea that I tried ended up looking pretty naff (and, anyway, I don't really like messing with the SMF aesthetic that a lot of veteran members prefer as-is). In this case, I think it's wiser to just do something really subtle and "quiet" (after all, the forum has been just fine for a long time without this, so I don't think it should be something that jumps out from the page and draws your eye too much; it should just be something that people who know what to look for, will look for). I've done this patch in two styles: Style AThis one just makes the subject-line of the linked-to post render differently than normal (with font-style: italic).  --- baseline/Sources/Display.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/Display.php 2024-04-04 00:02:44.000000000 +0000 @@ -230,24 +230,25 @@ SELECT COUNT(*) FROM {$db_prefix}messages WHERE ID_MSG < $virtual_msg AND ID_TOPIC = $topic", __FILE__, __LINE__); list ($context['start_from']) = mysql_fetch_row($request); mysql_free_result($request); } // We need to reverse the start as well in this case. $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $topicinfo['numReplies'] - $context['start_from']; $context['robot_no_index'] = true; + $context['requested_msg'] = $virtual_msg; } } // Create a previous next string if the selected theme has it as a selected option. $context['previous_next'] = $modSettings['enablePreviousNext'] ? '<a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new">' . $txt['previous_next_back'] . '</a> <a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=next#new">' . $txt['previous_next_forward'] . '</a>' : ''; // Check if spellchecking is both enabled and actually working. (for quick reply.) $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); // Censor the title... censorText($topicinfo['subject']); $context['page_title'] = $topicinfo['subject'];
--- baseline/Themes/default/Display.template.php 2010-10-22 01:38:35.000000000 +0000 +++ modified/Themes/default/Display.template.php 2024-04-04 00:03:41.000000000 +0000 @@ -359,32 +359,33 @@ echo ' <a href="', $scripturl, '?action=pm;sa=send;u=', $message['member']['id'], '" title="', $message['member']['online']['label'], '">', $settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/im_' . ($message['member']['online']['is_online'] ? 'on' : 'off') . '.gif" alt="' . $message['member']['online']['label'] . '" border="0" />' : $message['member']['online']['label'], '</a>'; } } // Otherwise, show the guest's email. elseif (empty($message['member']['hide_email'])) echo ' <br /> <br /> <a href="mailto:', $message['member']['email'], '">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/email_sm.gif" alt="' . $txt[69] . '" title="' . $txt[69] . '" border="0" />' : $txt[69]), '</a>'; // Done with the information about the poster... on to the post itself. + $is_requested_message = isset($context['requested_msg']) && $context['requested_msg'] == $message['id']; echo ' </div> </td> <td valign="top" width="85%" height="100%"> <table width="100%" border="0"><tr> <td valign="middle"><a href="', $message['href'], '"><img src="', $message['icon_url'] . '" alt="" border="0" /></a></td> <td valign="middle"> - <div style="font-weight: bold;" id="subject_', $message['id'], '"> + <div style="font-weight: bold;', $is_requested_message ? ' font-style: italic;' : '', '" id="subject_', $message['id'], '"> <a href="', $message['href'], '">', $message['subject'], '</a> </div>'; // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. echo ' <div class="smalltext">« <b>', !empty($message['counter']) ? $txt[146] . ' #' . $message['counter'] : '', ' ', $txt[30], ':</b> ', $message['time'], ' »</div></td> <td align="', !$context['right_to_left'] ? 'right' : 'left', '" valign="bottom" height="20" style="font-size: smaller;">'; // Can they reply? Have they turned on quick reply? if ($context['can_reply'] && !empty($options['display_quick_reply'])) echo ' <a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';num_replies=', $context['num_replies'], ';sesc=', $context['session_id'], '" onclick="doQuote(', $message['id'], ', \'', $context['session_id'], '\'); return false;">', $reply_button, '</a>';
Style BThis one places a small, low-opacity Unicode arrow just before the subject-line of the linked-to post.  --- baseline/Sources/Display.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/Display.php 2024-04-04 00:02:44.000000000 +0000 @@ -230,24 +230,25 @@ SELECT COUNT(*) FROM {$db_prefix}messages WHERE ID_MSG < $virtual_msg AND ID_TOPIC = $topic", __FILE__, __LINE__); list ($context['start_from']) = mysql_fetch_row($request); mysql_free_result($request); } // We need to reverse the start as well in this case. $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $topicinfo['numReplies'] - $context['start_from']; $context['robot_no_index'] = true; + $context['requested_msg'] = $virtual_msg; } } // Create a previous next string if the selected theme has it as a selected option. $context['previous_next'] = $modSettings['enablePreviousNext'] ? '<a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new">' . $txt['previous_next_back'] . '</a> <a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=next#new">' . $txt['previous_next_forward'] . '</a>' : ''; // Check if spellchecking is both enabled and actually working. (for quick reply.) $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); // Censor the title... censorText($topicinfo['subject']); $context['page_title'] = $topicinfo['subject'];
--- baseline/Themes/default/Display.template.php 2010-10-22 01:38:35.000000000 +0000 +++ modified/Themes/default/Display.template.php 2024-04-04 00:05:27.000000000 +0000 @@ -359,33 +359,34 @@ echo ' <a href="', $scripturl, '?action=pm;sa=send;u=', $message['member']['id'], '" title="', $message['member']['online']['label'], '">', $settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/im_' . ($message['member']['online']['is_online'] ? 'on' : 'off') . '.gif" alt="' . $message['member']['online']['label'] . '" border="0" />' : $message['member']['online']['label'], '</a>'; } } // Otherwise, show the guest's email. elseif (empty($message['member']['hide_email'])) echo ' <br /> <br /> <a href="mailto:', $message['member']['email'], '">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/email_sm.gif" alt="' . $txt[69] . '" title="' . $txt[69] . '" border="0" />' : $txt[69]), '</a>'; // Done with the information about the poster... on to the post itself. + $is_requested_message = isset($context['requested_msg']) && $context['requested_msg'] == $message['id']; echo ' </div> </td> <td valign="top" width="85%" height="100%"> <table width="100%" border="0"><tr> <td valign="middle"><a href="', $message['href'], '"><img src="', $message['icon_url'] . '" alt="" border="0" /></a></td> <td valign="middle"> <div style="font-weight: bold;" id="subject_', $message['id'], '"> - <a href="', $message['href'], '">', $message['subject'], '</a> + ', $is_requested_message ? '<span style="opacity: 30%;">⇾ </span>' : '', '<a href="', $message['href'], '">', $message['subject'], '</a> </div>'; // If this is the first post, (#0) just say when it was posted - otherwise give the reply #. echo ' <div class="smalltext">« <b>', !empty($message['counter']) ? $txt[146] . ' #' . $message['counter'] : '', ' ', $txt[30], ':</b> ', $message['time'], ' »</div></td> <td align="', !$context['right_to_left'] ? 'right' : 'left', '" valign="bottom" height="20" style="font-size: smaller;">'; // Can they reply? Have they turned on quick reply? if ($context['can_reply'] && !empty($options['display_quick_reply'])) echo ' <a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';num_replies=', $context['num_replies'], ';sesc=', $context['session_id'], '" onclick="doQuote(', $message['id'], ', \'', $context['session_id'], '\'); return false;">', $reply_button, '</a>';
As always, everyone's thoughts/notes/votes are appreciated.  @theymos: Style A doesn't add or remove any markup, so post scrapers (probably) won't be affected by the change. Style B introduces a new <span> element just before the <a> element that wraps the subject-line, so if that's the one you like (assuming that you like either one to begin with), then LoyceV and TryNinja would probably appreciate some notice before you merge it, so that they can tweak their scrapers if necessary.
|
|
|
This is something @libert19 suggested last year. I think it's a good idea: manually copy-pasting BBCode to update your signature is a bit error-prone (not everyone is as careful as they should be, and, although I don't really mess with mobile, I'm guessing that it's even more finicky on a phone/tablet). Besides that, it's just a nice, unobtrusive little time-saver (I bump into [code] blocks quite often on Bitcointalk, so I think this will be pretty handy beyond the use-case that inspired it). Thanks to @TryNinja for posting a userscript that I took a peek at as a starting point. I've put together two variations of this patch, here's what style A (default styling with just a hair of margin-bottom) looks like:  And here's what style B (the same border as [quote] blocks, which ends up working pretty nicely, IMO) looks like:  (I think style B works/fits a bit better, personally. Especially when there are small and/or multiple code blocks, like at the bottom of this post, I think style A will draw too much attention to itself.) Here's the diff for @theymos (style A): --- baseline/Sources/Subs.php 2011-09-17 21:59:55.000000000 +0000 +++ modified/Sources/Subs.php 2024-02-19 11:28:27.000000000 +0000 @@ -1123,7 +1123,7 @@ array( 'tag' => 'code', 'type' => 'unparsed_content', - 'content' => '<div class="codeheader">' . $txt['smf238'] . ':</div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', + 'content' => '<div class="codeheader">' . $txt['smf238'] . ': <button type="button" style="margin-bottom: 1px;" onclick="navigator.clipboard.writeText(this.parentElement.nextElementSibling.innerText);">Copy</button></div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', // !!! Maybe this can be simplified? 'validate' => isset($disabled['code']) ? null : create_function('&$tag, &$data, $disabled', ' global $context; @@ -1161,7 +1161,7 @@ array( 'tag' => 'code', 'type' => 'unparsed_equals_content', - 'content' => '<div class="codeheader">' . $txt['smf238'] . ': ($2)</div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', + 'content' => '<div class="codeheader">' . $txt['smf238'] . ': ($2) <button type="button" style="margin-bottom: 1px;" onclick="navigator.clipboard.writeText(this.parentElement.nextElementSibling.innerText);">Copy</button></div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', // !!! Maybe this can be simplified? 'validate' => isset($disabled['code']) ? null : create_function('&$tag, &$data, $disabled', ' global $context;
And here's the diff for style B: --- baseline/Sources/Subs.php 2011-09-17 21:59:55.000000000 +0000 +++ modified/Sources/Subs.php 2024-02-19 11:28:40.000000000 +0000 @@ -1123,7 +1123,7 @@ array( 'tag' => 'code', 'type' => 'unparsed_content', - 'content' => '<div class="codeheader">' . $txt['smf238'] . ':</div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', + 'content' => '<div class="codeheader">' . $txt['smf238'] . ': <button type="button" style="border: 1px solid #d0d0e0;" onclick="navigator.clipboard.writeText(this.parentElement.nextElementSibling.innerText);">Copy</button></div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', // !!! Maybe this can be simplified? 'validate' => isset($disabled['code']) ? null : create_function('&$tag, &$data, $disabled', ' global $context; @@ -1161,7 +1161,7 @@ array( 'tag' => 'code', 'type' => 'unparsed_equals_content', - 'content' => '<div class="codeheader">' . $txt['smf238'] . ': ($2)</div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', + 'content' => '<div class="codeheader">' . $txt['smf238'] . ': ($2) <button type="button" style="border: 1px solid #d0d0e0;" onclick="navigator.clipboard.writeText(this.parentElement.nextElementSibling.innerText);">Copy</button></div><div class="code">' . ($context['browser']['is_gecko'] ? '<pre style="margin-top: 0; display: inline;">$1</pre>' : '$1') . '</div>', // !!! Maybe this can be simplified? 'validate' => isset($disabled['code']) ? null : create_function('&$tag, &$data, $disabled', ' global $context;
@hd49728: Thanks for adding my patches to your topic, I appreciate it. I noticed that one of the titles is rendering incorrectly: Adding (non-breaking space) to the BBCode parser instead of Adding [nbsp] (non-breaking space) to the BBCode parser. That title probably rendered correctly at the time you added it, but, since that patch was accepted, [nbsp] has become valid BBCode, so now it needs to be escaped if you want it to display properly. You'll probably run into a similar problem with this title, too. Here's a tip: whenever you want a BBCode tag (or something that might become a BBCode tag) to render without being processed, remember to wrap it in [nobbc] tags (you can quote this post and examine this paragraph to see what I mean).
|
|
|
There's a small UI issue with the merit-sending page that makes it easier than it should be to accidentally send more merit than you intended (I'm sure I've run into a handful of anecdotes about this, but I can't find them now). Basically, the "Merit points" field is pre-populated with a 0, like this:  People are used to having to get rid of that 0 before entering an amount (or, they have to make sure that the amount they enter is after that 0). Sometimes, people are careless or tired, and accidentally enter something on the wrong side of that 0 (maybe believing that they've already deleted or selected it after mis-hitting a key, or something), and they end up sending more merit than they meant to (that is, sending 10, 20, 30, 40 or 50 merits by mistake is an easier thing to do than it should be). For comparison with what follows, the current HTML for that input looks like this: <input type="text" size="6" name="merits" value="0">. Here are some ways that I think this could be improved: Approach AThis approach would leave the field initially empty so that there's no 0 there to trip anyone up. That would look like this:  The HTML would change to: <input type="text" size="6" name="merits"> (that is, value="0" would be removed). Approach BThis approach would add a bit of JavaScript that "selects" the value whenever it's clicked, that way, whatever you type in will replace whatever was already there. That would look like this (after clicking):  The HTML would change to: <input type="text" size="6" name="merits" value="0" onclick="this.select();"> (that is, onclick="this.select();" would be added). Approach CThis approach would leave the 0 there, but make it "placeholder" text instead of actual text, that way, it's just there as a cue, but the field is actually empty (the 0 will disappear as soon as you type something in). That would look like this:  The HTML would change to: <input type="text" size="6" name="merits" placeholder="0"> (that is, value="0" would change to placeholder="0"). Closing thoughtsOne disadvantage of approach A is that it leaves the user wondering what they're meant to type in (that's obviously not an issue for people that have sent merit before, but I could see brand new users getting confused without any cues to help them). One disadvantage of approach B is that it requires JavaScript to function, and there's a (pretty small in this case, IMHO) chance that cross-browser weirdness will make it unreliable. One advantage of approach B is that it will catch a wider range of mistakes (sometimes people initially decide to send 1 merit, and then change that to a 2, but actually end up sending 12 or 21, by mistake. If the entire value gets selected each time you click the input, then those kinds of mistakes would be much harder to make). Approach C won't catch as many mistakes as approach B, but it's simple, won't leave new users confused, and doesn't need JavaScript to work. I would have included PHP diffs with this post, but I don't have access to the relevant code, so all I can do this time around is make suggestions like a noob. (I'm taking a break from the forum, but I'll be back on the 27th; try not to miss me too much.) 
|
|
|
This is a fix for an old issue that Foxpup diagnosed: The cause is that Arabic text automatically switches the text direction to right-to-left, and since the immediately following text is just numbers and punctuation marks, which can written in either direction, there's nothing to change it back.
Mods can fix it in this instance by adding a left-to-right mark (U+200E, ‎) to the end of the thread title, though that doesn't address the underlying bug.
Basically, topic titles ending with certain characters will cause the page numbers to mis-render, like this:  It's easy to find many examples of this on the Arabic and Hebrew local boards, like the following:  With this patch, the two previous examples would render like so:   Here's the diff for @theymos: --- baseline/Sources/MessageIndex.php 2011-12-22 22:56:39.000000000 +0000 +++ modified/Sources/MessageIndex.php 2023-10-02 14:44:40.000000000 +0000 @@ -495,32 +495,32 @@ // Decide how many pages the topic should have. $topic_length = $row['numReplies'] + 1; if ($topic_length > $modSettings['defaultMaxMessages']) { $tmppages = array(); $tmpa = 1; for ($tmpb = 0; $tmpb < $topic_length; $tmpb += $modSettings['defaultMaxMessages']) { $tmppages[] = '<a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . '.' . $tmpb . '">' . $tmpa . '</a>'; $tmpa++; } // Show links to all the pages? if (count($tmppages) <= 5) - $pages = '« ' . implode(' ', $tmppages); + $pages = '‎« ' . implode(' ', $tmppages); // Or skip a few? else - $pages = '« ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1]; + $pages = '‎« ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1]; if (!empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']) $pages .= ' <a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . '.0;all">' . $txt[190] . '</a>'; $pages .= ' »'; } else $pages = ''; // We need to check the topic icons exist... if (empty($modSettings['messageIconChecks_disable'])) { if (!isset($context['icon_sources'][$row['firstIcon']])) $context['icon_sources'][$row['firstIcon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['firstIcon'] . '.gif') ? 'images_url' : 'default_images_url'; if (!isset($context['icon_sources'][$row['lastIcon']]))
Edit: Adding a second diff because I noticed that this issue also shows up on ?action=unread, ?action=unreadreplies and ?action=watchlist. --- baseline/Sources/Recent.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/Recent.php 2023-10-06 04:52:26.000000000 +0000 @@ -981,32 +981,32 @@ // Decide how many pages the topic should have. $topic_length = $row['numReplies'] + 1; if ($topic_length > $modSettings['defaultMaxMessages']) { $tmppages = array(); $tmpa = 1; for ($tmpb = 0; $tmpb < $topic_length; $tmpb += $modSettings['defaultMaxMessages']) { $tmppages[] = '<a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . '.' . $tmpb . ';topicseen">' . $tmpa . '</a>'; $tmpa++; } // Show links to all the pages? if (count($tmppages) <= 5) - $pages = '« ' . implode(' ', $tmppages); + $pages = '‎« ' . implode(' ', $tmppages); // Or skip a few? else - $pages = '« ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1]; + $pages = '‎« ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1]; if (!empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']) $pages .= ' <a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . '.0;all">' . $txt[190] . '</a>'; $pages .= ' »'; } else $pages = ''; // We need to check the topic icons exist... you can never be too sure! if (empty($modSettings['messageIconChecks_disable'])) { // First icon first... as you'd expect. if (!isset($context['icon_sources'][$row['firstIcon']])) $context['icon_sources'][$row['firstIcon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['firstIcon'] . '.gif') ? 'images_url' : 'default_images_url';
|
|
|
I already posted about this ( here), but almost instantly regretted doing that (I like the pattern of creating a new topic for each SMF patch, so I'm not sure why I tried to do things differently this time).  Anyway, this one came about from joker_josue pointing out that receiving PMs with no subject is annoying, and Loyce (effectively) nodding his head and saying "patch time?".  It's not something that bugs me all that much (out of the 203 PMs I've received, 15 don't have a subject), but I can imagine it being really annoying for people with thousands of PMs in their inbox who find themselves looking back through them from time to time. Also, I've got some ideas for future patches that might allow for quickly isolating PMs by author, or subject, so phasing out (or, at least discouraging) PMs with "(No subject)" as their subject line, just seems like a good idea all-around to me. Currently, the subject line is pre-populated with the text "(No subject)", like this:  To stop lazy MFs from just going with the default, this patch leaves the subject line empty:  If you try to send (or preview) a PM without attempting to describe what you're about to bang on about, then you'll get an error, like this:  Here's the diff for @theymos: --- baseline/Sources/PersonalMessage.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/PersonalMessage.php 2023-09-16 00:58:49.000000000 +0000 @@ -1224,33 +1224,33 @@ $request = db_query(" SELECT realName FROM {$db_prefix}members WHERE ID_MEMBER IN (" . implode(', ', $_REQUEST['u']) . ") LIMIT " . count($_REQUEST['u']), __FILE__, __LINE__); while ($row = mysql_fetch_assoc($request)) $membersTo[] = '"' . $row['realName'] . '"'; mysql_free_result($request); } // Create the 'to' string - Quoting it, just in case it's something like bob,i,like,commas,man. $_REQUEST['to'] = implode(', ', $membersTo); } // Set the defaults... - $context['subject'] = $form_subject != '' ? $form_subject : $txt[24]; + $context['subject'] = $form_subject; $context['message'] = str_replace(array('"', '<', '>'), array('"', '<', '>'), $form_message); $context['to'] = isset($_REQUEST['to']) ? stripslashes($_REQUEST['to']) : ''; $context['bcc'] = isset($_REQUEST['bcc']) ? stripslashes($_REQUEST['bcc']) : ''; $context['post_error'] = array(); $context['copy_to_outbox'] = !empty($options['copy_to_outbox']); // And build the link tree. $context['linktree'][] = array( 'url' => $scripturl . '?action=pm;sa=send', 'name' => $txt[321] ); $context['visual_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']; if ($context['visual_verification']) { $context['use_graphic_library'] = in_array('gd', get_loaded_extensions());
Edit: Adding a second diff which I think improves this patch quite a bit (read more below). --- baseline/Themes/default/PersonalMessage.template.php 2006-12-01 15:43:03.000000000 +0000 +++ modified/Themes/default/PersonalMessage.template.php 2023-10-31 02:09:46.000000000 +0000 @@ -910,33 +910,33 @@ <td align="right"><b', (isset($context['post_error']['no_to']) || isset($context['post_error']['bad_to']) ? ' style="color: red;"' : ''), '>', $txt[150], ':</b></td> <td class="smalltext"> <input type="text" name="to" id="to" value="', $context['to'], '" tabindex="', $context['tabindex']++, '" size="40" /> <a href="', $scripturl, '?action=findmember;input=to;quote=1;sesc=', $context['session_id'], '" onclick="return reqWin(this.href, 350, 400);"><img src="', $settings['images_url'], '/icons/assist.gif" alt="', $txt['find_members'], '" /></a> <a href="', $scripturl, '?action=findmember;input=to;quote=1;sesc=', $context['session_id'], '" onclick="return reqWin(this.href, 350, 400);">', $txt['find_members'], '</a> </td> </tr><tr> <td align="right"><b', (isset($context['post_error']['bad_bcc']) ? ' style="color: red;"' : ''), '>', $txt[1502], ':</b></td> <td class="smalltext"> <input type="text" name="bcc" id="bcc" value="', $context['bcc'], '" tabindex="', $context['tabindex']++, '" size="40" /> <a href="', $scripturl, '?action=findmember;input=bcc;quote=1;sesc=', $context['session_id'], '" onclick="return reqWin(this.href, 350, 400);"><img src="', $settings['images_url'], '/icons/assist.gif" alt="', $txt['find_members'], '" /></a> ', $txt[748], ' </td> </tr>'; // Subject of personal message. echo ' <tr> <td align="right"><b', (isset($context['post_error']['no_subject']) ? ' style="color: red;"' : ''), '>', $txt[70], ':</b></td> - <td><input type="text" name="subject" value="', $context['subject'], '" tabindex="', $context['tabindex']++, '" size="40" maxlength="50" /></td> + <td><input type="text" name="subject" placeholder="What does this message concern?" value="', $context['subject'], '" tabindex="', $context['tabindex']++, '" size="40" maxlength="50" /></td> </tr>'; if ($context['visual_verification']) { echo ' <tr> <td align="right" valign="top"> <b>', $txt['pm_visual_verification_label'], ':</b> </td> <td>'; if ($context['use_graphic_library']) echo ' <img src="', $context['verificiation_image_href'], '" alt="', $txt['pm_visual_verification_desc'], '" /><br />'; else echo ' <img src="', $context['verificiation_image_href'], ';letter=1" alt="', $txt['pm_visual_verification_desc'], '" />
People get attached to how things look (I know I do), and it occurred to me that three empty <input> elements in a row might look a bit stark. I think including a placeholder attribute on the third <input> element will do a good job of balancing the issue this patch is meant to address (PMs without subjects) against imitating the way it looked originally (that is, two empty <input> elements, followed by a third one with something already in it). That way, it looks similar, but PMs will still fail to send unless you actually type something in. Here's how it would look (I think the text I chose is short and to the point, but theymos can/should adjust it if he wants to):  Note to theymos: This new diff doesn't replace the earlier one, and they should be merged as a pair (the first diff stops the subject from being pre-populated with "(No subject)", and the second diff adds the placeholder attribute explained above).
|
|
|
There was a recent Meta thread about the auto-linker sometimes failing to properly recognize URLs, and my name came up, so I decided to poke around and see if I could make sense of this bug. As a recap, the auto-linker can sometimes be confused by leading spaces (particularly after a post has been edited, or quoted). For example, if you post the following (a sequence of URLs with an increasing amount of leading space, meant to showcase the problem): www.thefarside.com www.thefarside.com www.thefarside.com www.thefarside.com www.thefarside.com www.thefarside.com www.thefarside.com www.thefarside.com Then it'll (initially) render correctly, like this:  But after an edit (even one that doesn't change anything), it'll render incorrectly, like this (i.e. links with 2/4/6 leading spaces no longer recognized):  If the original post is quoted, then it'll render like this (i.e. links with 3/5/7 leading spaces no longer recognized):  (And if the quoted post were edited, it would revert to links with 2/4/6 leading spaces no longer being recognized.) Pretty weird, huh? Now, I know there are a few places in SMF where whitespace conversions happen (that's part of the reason I did the [nbsp] patch, so that non-breaking spaces could be used in a way that wouldn't be undone by those conversions). So, I don't find this bug that perplexing (though, I was surprised that the bug persisted even after bypassing preparsecode() and un_preparsecode(); I had figured that something in one of those two functions was behind spacing not "round-tripping" correctly on SMF). Anyway, regardless of the ultimate source(s) of spacing getting silently messed with when you edit (or quote) a post, this particular bug is caused by the URL regexes in the auto-linker not properly taking this state of affairs into account (which is odd, because the e-mail regexes do). Specifically, the positive lookbehind assertions aren't aware of non-breaking spaces (and the second regex, the one for schemeless URLs, needs an additional tweak in order to prevent this bug from sometimes presenting during post preview). Here's the diff for @theymos: --- baseline/Sources/Subs.php 2011-09-17 21:59:55.000000000 +0000 +++ modified/Sources/Subs.php 2023-09-07 15:04:45.000000000 +0000 @@ -1820,36 +1820,39 @@ // Don't go backwards. //!!! Don't think is the real solution.... $lastAutoPos = isset($lastAutoPos) ? $lastAutoPos : 0; if ($pos < $lastAutoPos) $no_autolink_area = true; $lastAutoPos = $pos; if (!$no_autolink_area) { // Parse any URLs.... have to get rid of the @ problems some things cause... stupid email addresses. if (!isset($disabled['url']) && (strpos($data, '://') !== false || strpos($data, 'www.') !== false)) { // Switch out quotes really quick because they can cause problems. $data = strtr($data, array(''' => '\'', ' ' => $context['utf8'] ? "\xC2\xA0" : "\xA0", '"' => '>">', '"' => '<"<', '<' => '<lt<')); + // Can't make use of $non_breaking_space in the URL regexes (that definition won't work without the "u" modifier). + $nbsp = $context['utf8'] ? '\xc2\xa0' : '\xa0'; + // Only do this if the preg survives. if (is_string($result = preg_replace(array( - '~(?<=[\s>\.(;\'"]|^)((?:http|https|ftp|ftps)://[\w\-_%@:|]+(?:\.[\w\-_%]+)*(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i', - '~(?<=[\s>(\'<]|^)(www(?:\.[\w\-_]+)+(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i' + '~(?<=[\s>\.(;\'"]|' . $nbsp . '|^)((?:http|https|ftp|ftps)://[\w\-_%@:|]+(?:\.[\w\-_%]+)*(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i', + '~(?<=[\s>(;\'<]|' . $nbsp . '|^)(www(?:\.[\w\-_]+)+(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#(){}+:\'\\\\]*)*[/\w\-_\~%@\?;=#}\\\\])~i' ), array( '[url]$1[/url]', '[url=http://$1]$1[/url]' ), $data))) $data = $result; $data = strtr($data, array('\'' => ''', $context['utf8'] ? "\xC2\xA0" : "\xA0" => ' ', '>">' => '"', '<"<' => '"', '<lt<' => '<')); } // Next, emails... if (!isset($disabled['email']) && strpos($data, '@') !== false) { $data = preg_replace('~(?<=[\?\s' . $non_breaking_space . '\[\]()*\\\;>]|^)([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?,\s' . $non_breaking_space . '\[\]()*\\\]|$|<br />| |>|<|"|'|\.(?:\.|;| |\s|$|<br />))~' . ($context['utf8'] ? 'u' : ''), '[email]$1[/email]', $data); $data = preg_replace('~(?<=<br />)([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?\.,;\s' . $non_breaking_space . '\[\]()*\\\]|$|<br />| |>|<|"|')~' . ($context['utf8'] ? 'u' : ''), '[email]$1[/email]', $data); } }
(Because this patch amounts to adjusting a pair of regexes in the BBCode parser, it will both fix this bug moving forward, and retroactively fix old posts that have unclickable links in them due to this issue, like this one.)
|
|
|
This is something I've been working on since chatting with @TryNinja and @LoyceV about how they repair holes in their post archives after an outage. The way SMF combines topic IDs with message IDs makes it very awkward for scrapers to fill in gaps, because even though they know what ranges of message IDs they're missing, they often don't know what the corresponding topic IDs are. Normally, the link format for a given (anchored) message, looks like this: /index.php?topic={topic_id}.msg{message_id}#msg{message_id}This patch changes SMF so that it will also accept an asterisk [1] as the topic ID: /index.php?topic=*.msg{message_id}#msg{message_id}Here's the diff for @theymos: --- baseline/Sources/QueryString.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Sources/QueryString.php 2023-08-23 12:36:25.000000000 +0000 @@ -65,41 +65,41 @@ - makes sure a string only contains character which are allowed in XML/XHTML (not 0-8, 11, 12, and 14-31.) - tries to handle UTF-8 properly, and shouldn't negatively affect character sets like ISO-8859-1. - does not effect keys, only changes values. - may call itself recursively if necessary. string ob_sessrewrite(string buffer) - rewrites the URLs outputted to have the session ID, if the user is not accepting cookies and is using a standard web browser. - handles rewriting URLs for the queryless URLs option. - can be turned off entirely by setting $scripturl to an empty string, ''. (it wouldn't work well like that anyway.) - because of bugs in certain builds of PHP, does not function in versions lower than 4.3.0 - please upgrade if this hurts you. */ // Clean the request variables - add html entities to GET and slashes if magic_quotes_gpc is Off. function cleanRequest() { - global $board, $topic, $boardurl, $scripturl, $modSettings; + global $board, $topic, $boardurl, $scripturl, $modSettings, $db_prefix; // Makes it easier to refer to things this way. $scripturl = $boardurl . '/index.php'; // Save some memory.. (since we don't use these anyway.) unset($GLOBALS['HTTP_POST_VARS'], $GLOBALS['HTTP_POST_VARS']); unset($GLOBALS['HTTP_POST_FILES'], $GLOBALS['HTTP_POST_FILES']); // These keys shouldn't be set...ever. if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])) die('Invalid request variable.'); // Same goes for numeric keys. foreach (array_merge(array_keys($_POST), array_keys($_GET), array_keys($_FILES)) as $key) if (is_numeric($key)) die('Invalid request variable.'); // Numeric keys in cookies are less of a problem. Just unset those. foreach ($_COOKIE as $key => $value) if (is_numeric($key)) @@ -214,40 +214,49 @@ else $board = 0; // If there's a threadid, it's probably an old YaBB SE link. Flow with it. if (isset($_REQUEST['threadid']) && !isset($_REQUEST['topic'])) $_REQUEST['topic'] = $_REQUEST['threadid']; // We've got topic! if (isset($_REQUEST['topic'])) { // Make sure that its a string and not something else like an array $_REQUEST['topic'] = (string)$_REQUEST['topic']; // Slash means old, beta style, formatting. That's okay though, the link should still work. if (strpos($_REQUEST['topic'], '/') !== false) list ($_REQUEST['topic'], $_REQUEST['start']) = explode('/', $_REQUEST['topic']); // Dots are useful and fun ;). This is ?topic=1.15. elseif (strpos($_REQUEST['topic'], '.') !== false) list ($_REQUEST['topic'], $_REQUEST['start']) = explode('.', $_REQUEST['topic']); + // If a message ID was given with a topic ID of '*', then search for (and use) the correct topic ID. + if($_REQUEST['topic'] == '*' && !empty($_REQUEST['start']) && substr($_REQUEST['start'], 0, 3) == 'msg') + { + $result = db_query('SELECT ID_TOPIC FROM ' . $db_prefix . 'messages WHERE ID_MSG = ' . (int)substr($_REQUEST['start'], 3), __FILE__, __LINE__); + $row = mysql_fetch_row($result); + mysql_free_result($result); + $_REQUEST['topic'] = !empty($row) ? (int)$row[0] : -1; + } + $topic = (int) $_REQUEST['topic']; // Now make sure the online log gets the right number. $_GET['topic'] = $topic; } else $topic = 0; // There should be a $_REQUEST['start'], some at least. If you need to default to other than 0, use $_GET['start']. if (empty($_REQUEST['start']) || $_REQUEST['start'] < 0 || (int) $_REQUEST['start'] > 2147473647) $_REQUEST['start'] = 0; // The action needs to be a string and not an array or anything else if (isset($_REQUEST['action'])) $_REQUEST['action'] = (string) $_REQUEST['action']; if (isset($_GET['action'])) $_GET['action'] = (string) $_GET['action']; // Store the REMOTE_ADDR for later - even though we HOPE to never use it... $_SERVER['BAN_CHECK_IP'] = isset($_SERVER['REMOTE_ADDR']) && preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['REMOTE_ADDR']) === 1 ? $_SERVER['REMOTE_ADDR'] : 'unknown';
(I made a previous attempt at this, based on HTTP redirects that would've meant a maximum of 30 requests per minute because of the rate-limiter. I'm hoping that this new redirection-free attempt meets with theymos' approval and ends up simplifying LoyceV's and TryNinja's scrapers and making them more reliable.)[1] An asterisk (*) makes sense to me, and is safe to use according to my reading of RFC 3986, but zero (0) and underscore (_) both make sense too, I guess. (It's easy for theymos to adjust.)
|
|
|
This one is pretty unexciting, but I like doing these seemingly-unimportant small improvements (they're quick to do and I believe that they add up over time). The four problems with this tag (as I see it) are as follows: 1. When you click the "Glow" button on the toolbar, you get this: [glow=red,2,300][/glow]. I don't think red is a good default (mostly I see this tag used with yellow, Husires suggested changing the default last year, and I think that's a good suggestion). 2. The extra stuff after the color ( ,2,300) has to do with an ancient Microsoft-specific CSS filter, and the final parameter ( ,300) isn't actually wired up to anything. 3. The example on the help page is broken (because the pre-rendered HTML is based on the old Microsoft-specific CSS filter). 4. The toolbar button being green doesn't make much sense (maybe it did when this tag produced a true glow effect on Internet Explorer, but now all this tag does is set the background-color on a <span>, so it makes more sense for it to match whatever the default color is, IMO). The patch below removes the extra parameters (without breaking old posts), sets the default to [glow=yellow][/glow], updates/repairs the example on the help page, and changes the icon so that it's yellow instead of green (  ). --- baseline/Sources/Subs.php 2011-09-17 21:59:55.000000000 +0000 +++ modified/Sources/Subs.php 2023-08-16 15:41:43.000000000 +0000 @@ -1273,20 +1273,27 @@ 'after' => '</span>', ), array( 'tag' => 'glow', 'type' => 'unparsed_commas', 'test' => '[#0-9a-zA-Z\-]{3,12},([012]\d{1,2}|\d{1,2})(,[^]]+)?\]', 'before' => $context['browser']['is_ie'] ? '<table border="0" cellpadding="0" cellspacing="0" style="display: inline; vertical-align: middle; font: inherit;"><tr><td style="filter: Glow(color=$1, strength=$2); font: inherit;">' : '<span style="background-color: $1;">', 'after' => $context['browser']['is_ie'] ? '</td></tr></table> ' : '</span>', ), array( + 'tag' => 'glow', + 'type' => 'unparsed_equals', + 'test' => '(#[\da-fA-F]{3}|#[\da-fA-F]{6}|[A-Za-z]{1,12})\]', + 'before' => '<span style="background-color: $1;">', + 'after' => '</span>', + ), + array( 'tag' => 'hr', 'type' => 'closed', 'content' => '<hr />', 'block_level' => true, ), array( 'tag' => 'html', 'type' => 'unparsed_content', 'content' => '$1', 'block_level' => true,
--- baseline/Themes/default/Post.template.php 2008-04-30 18:30:34.000000000 +0000 +++ modified/Themes/default/Post.template.php 2023-08-16 15:44:50.000000000 +0000 @@ -752,21 +752,21 @@ // ]]></script>'; // The below array makes it dead easy to add images to this page. Add it to the array and everything else is done for you! $context['bbc_tags'] = array(); $context['bbc_tags'][] = array( 'bold' => array('code' => 'b', 'before' => '[b]', 'after' => '[/b]', 'description' => $txt[253]), 'italicize' => array('code' => 'i', 'before' => '[i]', 'after' => '[/i]', 'description' => $txt[254]), 'underline' => array('code' => 'u', 'before' => '[u]', 'after' => '[/u]', 'description' => $txt[255]), 'strike' => array('code' => 's', 'before' => '[s]', 'after' => '[/s]', 'description' => $txt[441]), array(), - 'glow' => array('code' => 'glow', 'before' => '[glow=red,2,300]', 'after' => '[/glow]', 'description' => $txt[442]), + 'glow' => array('code' => 'glow', 'before' => '[glow=yellow]', 'after' => '[/glow]', 'description' => $txt[442]), 'shadow' => array('code' => 'shadow', 'before' => '[shadow=red,left]', 'after' => '[/shadow]', 'description' => $txt[443]), 'move' => array('code' => 'move', 'before' => '[move]', 'after' => '[/move]', 'description' => $txt[439]), array(), 'pre' => array('code' => 'pre', 'before' => '[pre]', 'after' => '[/pre]', 'description' => $txt[444]), 'left' => array('code' => 'left', 'before' => '[left]', 'after' => '[/left]', 'description' => $txt[445]), 'center' => array('code' => 'center', 'before' => '[center]', 'after' => '[/center]', 'description' => $txt[256]), 'right' => array('code' => 'right', 'before' => '[right]', 'after' => '[/right]', 'description' => $txt[446]), array(), 'hr' => array('code' => 'hr', 'before' => '[hr]', 'description' => $txt[531]), array(),
--- baseline/Themes/default/Help.template.php 2006-12-01 03:25:52.000000000 +0000 +++ modified/Themes/default/Help.template.php 2023-08-16 15:48:08.000000000 +0000 @@ -1780,23 +1780,23 @@ <td><img onmouseover="bbc_highlight(this, true);" onmouseout="bbc_highlight(this, false);" src="', $settings['images_url'], '/bbc/strike.gif" alt="', $txt['manual_posting_bbc_strike'], '" style="background-image: url(', $settings['images_url'], '/bbc/bbc_bg.gif); margin: 1px 2px 1px 1px;" /></td> <td>', $txt['manual_posting_strike_code'], '</td> <td><s>', $txt['manual_posting_strike_output'], '</s></td> <td>', $txt['manual_posting_strike_comment'], '</td> </tr> <tr> <td>', $txt['manual_posting_bbc_glow'], '</td> <td><img onmouseover="bbc_highlight(this, true);" onmouseout="bbc_highlight(this, false);" src="', $settings['images_url'], '/bbc/glow.gif" alt="', $txt['manual_posting_bbc_glow'], '" style="background-image: url(', $settings['images_url'], '/bbc/bbc_bg.gif); margin: 1px 2px 1px 1px;" /></td> <td>', $txt['manual_posting_glow_code'], '</td> <td> - <div style="filter: Glow(color=red, strength=2); width: 30px;"> + <span style="background-color: yellow;"> ', $txt['manual_posting_glow_output'], ' - </div> + </span> </td> <td>', $txt['manual_posting_glow_comment'], '</td> </tr> <tr> <td>', $txt['manual_posting_bbc_shadow'], '</td> <td><img onmouseover="bbc_highlight(this, true);" onmouseout="bbc_highlight(this, false);" src="', $settings['images_url'], '/bbc/shadow.gif" alt="', $txt['manual_posting_bbc_shadow'], '" style="background-image: url(', $settings['images_url'], '/bbc/bbc_bg.gif); margin: 1px 2px 1px 1px;" /></td> <td>', $txt['manual_posting_shadow_code'], '</td> <td> <div style="filter: Shadow(color=red, direction=240); width: 30px;"> ', $txt['manual_posting_shadow_output'], '
--- baseline/Themes/default/languages/Manual.english.php 2011-02-07 16:45:09.000000000 +0000 +++ modified/Themes/default/languages/Manual.english.php 2023-08-16 15:50:43.000000000 +0000 @@ -463,23 +463,23 @@ $txt['manual_posting_italic_comment'] = '*'; $txt['manual_posting_bbc_underline'] = 'Underline'; $txt['manual_posting_underline_code'] = '[u]underline[/u]'; $txt['manual_posting_underline_output'] = 'underline'; $txt['manual_posting_underline_comment'] = '*'; $txt['manual_posting_bbc_strike'] = 'Strikethrough'; $txt['manual_posting_strike_code'] = '[s]strikethrough[/s]'; $txt['manual_posting_strike_output'] = 'strikethrough'; $txt['manual_posting_strike_comment'] = '*'; $txt['manual_posting_bbc_glow'] = 'Glow'; -$txt['manual_posting_glow_code'] = '[glow=red,2,50]glow[/glow]'; +$txt['manual_posting_glow_code'] = '[glow=yellow]glow[/glow]'; $txt['manual_posting_glow_output'] = 'glow'; -$txt['manual_posting_glow_comment'] = 'The three attributes (eg red, 2, 50) in the \'glow\' tag are color, strength and width respectively.'; +$txt['manual_posting_glow_comment'] = '*'; $txt['manual_posting_bbc_shadow'] = 'Shadow'; $txt['manual_posting_shadow_code'] = '[shadow=red,left]<br />shadow<br />[/shadow]'; $txt['manual_posting_shadow_output'] = 'shadow'; $txt['manual_posting_shadow_comment'] = 'The two attributes (eg red, left) in the \'shadow\' tag are color and direction respectively.'; $txt['manual_posting_bbc_move'] = 'Marquee'; $txt['manual_posting_move_code'] = '[move]move[/move]'; $txt['manual_posting_move_output'] = 'move'; $txt['manual_posting_move_comment'] = 'Not valid XHTML, but can also be used for images!'; $txt['manual_posting_bbc_pre'] = 'Preformatted Text'; $txt['manual_posting_pre_comment'] = 'Preserves critical text formatting, rendered in a monospace font.';
This is the command sequence I used to produce the yellow version of the toolbar icon from the original (the second command requires ImageMagick): $ cp ./Themes/default/images/bbc/glow.gif ./Themes/default/images/bbc/glow_original.gif $ convert-im6 ./Themes/default/images/bbc/glow_original.gif -color-matrix '0 1.33 0 0 1.33 0 0 0 0' ./Themes/default/images/bbc/glow.gif
|
|
|
Hey, everybody!  So, as some of you know, I've been working on adding (optional) 2FA to the forum for a while now. I mostly finished this work late last year, and it's just been sitting in a folder, waiting for its day in the sun.  I've finally put the finishing touches to this one, and sent it off to theymos... Most of my patches don't end up getting merged, and some of the time that's because of the difficulty in recasting diffs made against SMF 1.1.19 into a form suitable for the forum's customized version of SMF. This time around I thought I'd try a different approach, so I put the bulk of the code in a new file: TOTP.php, and then included a small "example integration" of how this new file might be wired up to the rest of SMF. The idea here is that rather than making all of the design decisions myself (e.g. how the settings UI should look/work, how it should interact with password resets, etc.) I've instead focused on giving theymos a bespoke set of 2FA primitives and a working example of how to use them. This patch took around 90 hours to design, develop and test. I generally prefer to write code from scratch when I have the time, and that preference worked out very well for this project; I was able to whittle things down to a lot less (total) code than if I had pulled in any dependencies. I know this post could use more info (and some images), but I expect a fair amount of iteration to happen based on theymos' feedback, so I'll describe the system in more detail once things firm up. In the meantime, if anyone has any questions about the implementation (as it currently stands), then I'm more than happy to answer them.  On a more serious note, I find a lot of the dismissive attitude around 2FA to be quite confusing. When I'm digesting old topics about this issue (say, from before 2017, or so) the vibe I generally get from that group of users is that (optional) TOTP would be a really nice thing to have, full stop. But recently the sentiment seems to have switched, and instead of admitting that it would be a nice option to have, people seem very focused on picking it apart and reminding whoever posts about it that it's not a silver bullet, etc. I'm not sure why the average opinion changed like that (maybe because 2FA on SMF became such an unlikely idea that people started "defending" the lack of it?), but I align with the more optimistic group and think that a correctly implemented TOTP option makes a ton of sense (even for SMF). Beyond the obvious advantages for the people that enable it (like making their accounts all but impossible to "phish"), I'm hopeful that it might also help with incidents like this (which is what motivated me to roll up my sleeves in the first place).
|
|
|
|