azurelunatic: A castle with rockets and fire cannons with the DW D on it. (Castle Dreamwidth)
Azure Jane Lunatic (Azz) 🌺 ([personal profile] azurelunatic) wrote in [site community profile] dw_dev2017-04-29 03:06 pm
Entry tags:

Code tour: 2017-04-02 to 2017-04-29

So we've got a code push coming up tomorrow! The code push will include stuff from mid-February until now. With one small exception, most of this is not yet live on the site. (The exception is the stuff that [staff profile] mark faithfully checked in, so the repository matches what's live in production.)

This tour covers April. It was a busy, busy month: 53 total issues resolved
Contributors: [ profile] NightFlyer, [ profile] afuna, [ profile] kaberett, [ profile] kareila, [ profile] rahaeli, [ profile] srukle, [ profile] zorkian

[staff profile] karzilla was doing something akin to NaNoWriMo for development, and it shows!

This is [ profile] NightFlyer's first contribution! Welcome!

Issue 117: Disable our /interface/github handler (pull request) (nonfree pull request)
Category: the tools to make the tools, making things make sense
Patch by: [ profile] kareila
Entire tool by: [ profile] afuna
Description: [personal profile] fu is super cool. Not that this isn't true in general, but this time it's specifically because Fu has made a really sweet tool.
Dreamwidth uses Github Issues to organize and track things that need to be done, and who is currently doing them. You might think that in a sensible world, people should be able to just say "Me, I'll do it!" and lo and behold, they can mark themselves down as working on it. Github ... doesn't quite work like that. The ability to assign issues to people comes with some other powers that the average contributor doesn't need (and shouldn't have). (The principle of least privilege says that you do not give the thirteen-year-old goof-off who is painting your fence access to the $669.00 high-pressure power paint sprayer on the heavy-duty rolling cart, you give them a bucket, a roller, and one of those little paint trays. And adult supervision.)
Enter the bot. The bot sees all, knows all, and when you've been added to the list and say "Claimed!" on a ticket, the bot will helpfully assign it to you.
This patch is to switch from a previous version of the bot, to Fu's shiny shiny version. The bonus of using Fu's tool is, even though this patch is in dw-nonfree (other sites can look, but not touch, but they probably wouldn't want to anyway), Fu's version is its own little project which people can use as they wish on their own sites! Yay!

Issue 1150: investigate mixed content issues in /latest
Category: HTTPS Everywhere v2
Patch by: [ profile] afuna
Description: We've been working on getting HTTPS across the site for a long, long time. (For those who need a refresher, HTTPS encrypts the conversation between your browser and Dreamwidth, so that unassuming-looking person in the corner of the coffee shop with the beat-up laptop who is totally taking advantage of the free insecure wifi to steal people's bank passwords, will only see gibberish instead of that nice piece of art someone posted. Unless they come and peer over your shoulder. It's very hard to encrypt that.) So back in 2015, there were "mixed content" (some https, some not-https, all on the same page!) warnings for the Latest Things page.
Things got implemented, things got turned on, and [personal profile] fu left a reminder to herself to double-check once the cache had cleared.
Two years later, [staff profile] denise confirms: the cache is cleared.

Issue 1189: "Task no longer known to Gearman" timeouts randomly on payments
Category: Oh, computers, payments
Description: Online shopping carts and payment systems are difficult. There was a thing where somewhere in the payment process, the thingy that was supposed to be keeping track of the shopping cart just sort of ... upended itself. Various people (Mark, Fu) tried various things to fix it. When last we heard, it had been fixed to the point where instead of going "Hey, so I got your money but ... I have no idea what you just paid for, so you'll have to contact Support???" it would go "Hey, so I completely lost your cart, but at least I didn't charge your card! Please try that again?" That was not great either. But somewhere along the line, it mostly seemed to stop happening! Hooray!

Issue 1712: success message for buying paid time when not logged in needs to be changed to reflect no receipt will be sent (pull request)
Category: payments, privacy, making things make sense
Patch by: [ profile] kareila
Description: Did you know that you can buy paid time when not logged in? I'm not super clear on how the workflow goes, but it's a thing that can be done. If, say, you wanted to have the benefits of a paid account, but didn't want anyone to know that lesbiansuffragist1860 was associated with the credit card name of Jane Addams, you might buy some points while logged out. But the success message implied that you'd be getting an email. This is incorrect. Logged-out purchases do not collect email addresses. A different and more accurate message will be displayed instead.

Issue 1755: Success message error when deleting an account
Category: duplicate
Patch by: [staff profile] karzilla
Description: Sometimes a rare problem happens often enough that two people independently file it. This rare weird error message when trying to delete an account (the deletion worked, but something weird happened) was one of them. It's fixed elsewhere.

Issue 1713: allow embeds from jsfiddle (pull request)
Category: embeds
Patch by: [ profile] srukle
Description: JSFiddle, as the name implies, lets you fiddle around with javascript and test things. Now you can show your code, but you're still not allowed to run arbitrary scripts on Dreamwidth. (After all, you're not [ profile] whitaker, and this isn't LJ.)

Issue 1854: Email posting: post-security tag not working (possibly under some conditions) (pull request)
Category: email is hard, post-by-email, images!!!, Stand back! I know regular expressions!
Patch by: [ profile] kareila
Description: Back in August, when I was quivering in terror about having to go in for (minor) surgery, I wrote "If lost, return to: " and then the email address of my partner. I took a picture. At that point in time, many more things in our lives were complicated, and the best way to show them the picture was in a very, very locked Dreamwidth entry. I didn't feel like contending with the image upload page on top of everything else, so I emailed it in, with the appropriate sigils to make it post to a specific custom security group composed of only my partner.
Unfortunately, the picture posted ... not to that security group. (Fortunately, nothing bad happened.) Other people with more time to make reports had this happen to them, too. [staff profile] karzilla still knows regular expressions, and improved the post-by-email system so this will stop happening to people.

Issue 1921: Suppress tag creation errors on community import (pull request)
Category: communities, the importer has tentacles, 2000 tags is enough for (almost) anyone
Patch by: [ profile] kareila
Description: There was a very specific situation where importing a community could lead to the admin doing the importing getting potentially thousands of bogus warnings about tags in their inbox. Ordinarily, it's a good idea to warn someone if they're trying to post an entry but they have tags on it that won't be created. When they're importing a community and there are "ghost tags" still attached to the entry is not the time or the place.

Issue 1950: receipt not sent to buyer when gift is made
Category: payments
Patch by: n/a
Description: After some examination, it turns out that users who bought paid time as a gift and then didn't get a receipt were probably the unlucky recipient of email-related troubles (possibly That Email Provider We Loathe Eating Your Email Again), not "we say we're sending this to you but we only send it to your recipient" troubles.

Issue 1966: finish converting htdocs/editicons.bml (pull request)
Category: BML Conversion, icons are nice
Patch by: [ profile] kareila
Description: The stuff controlling icon uploads was very, very old. This modernization makes it possible to do other cool things later.

Issue 1973: allow embeds from (pull request)
Category: embeds, shiny things
Patch by: [ profile] srukle
Description: appears to be some sort of short looping video site. Embeds from them will therefore contain short, looping, and hopefully entertaining videos.

Issue 1996: change expiration email for premium paid accounts (pull request)
Category: payments, making things make sense
Patch by: [ profile] kareila
Description: Payments always involve a lot of moving pieces. This moving piece is a warning about renewals as regular-paid when someone's account is premium-paid. With any luck, this particular renewal reminder will answer some questions before they can be asked.

Issue 1997: add medium (pull request)
Category: profile information, other sites
Patch by: [ profile] srukle
Description: Perhaps you also publish articles on, as well as having a Dreamwidth account? Now you can link that from your profile, if so.

Issue 2006: import failure messages should include the journal name (pull request)
Category: importer, making things make sense
Patch by: [ profile] kareila
Description: Perhaps you are one of the several people who had ten LiveJournal accounts and a handful of InsaneJournal accounts, and maybe a few on other clone sites as well. If you tried to import six LiveJournal accounts and a half-dozen LiveJournal communities, and one of them had some error in importing -- which one was it? This change makes the error message be more clear about exactly which account had something go wrong with the import.

Issue 2022: investigate inconsistent application of top padding for embedded iframes (pull request)
Category: embeds, previews
Patch by: [ profile] kareila
Description: Previewing some embeds didn't look quite right (extra space at the top). That was because they weren't being displayed quite right on the preview. Now they will be.

Issue 2030: feed creation form doesn't check to make sure account name already exists (pull request)
Category: back end, warnings, syndication
Patch by: [ profile] kareila
Description: If you like to look at beautiful pictures of space, may I recommend ? Or, if you prefer to view that sort of content on your reading page, you can create a feed. Or ... you could, if someone else hadn't already created it: [syndicated profile] nasa_imageoftheday_feed. One of the things that checks for duplicates like that wasn't quite running in the correct order, and that resulted in warnings in the logs. And when there are warnings in the logs, here comes [staff profile] karzilla, to clear up the misunderstandings.

Issue 2041: can't import icons from LJ: code error (pull request)
Category: importer, icons, images!!!
Patch by: [ profile] kareila
Description: If one of your icons on LiveJournal got eaten by Frank the Goat, that could mess up your entire icon import. The new image back-end didn't quite know what to do about icons that don't exist anymore. Now it will start politely ignoring them, so the other ones should still come over successfully.

Issue 2045: Whitelist embeds from (pull request)
Category: embeds
Patch by: [ profile] kareila
Description: More shiny (and potentially loud) embed sources.

Issue 2046: should detect & diagnose old versions of Compass (pull request)
Category: modernization is fun for the whole family, back end shenanigans
Patch by: [ profile] kareila
Description: Compass is a tool that makes it easier to build websites that don't have their text trying to fall down on you like buildings in a collapsing dream from Inception, or something. In any case, it's something where people maybe shouldn't be using a super old version. This checks to see that the version is at least as new as the version [staff profile] karzilla is using.

Issue 2055: increase size for icon description field (pull request)
Category: alt text is important
Patch by: [ profile] rahaeli
Description: Image descriptions are important for people who are browsing without images, both by choice and not by choice. Some icons need a little extra space to describe. This gives 300 (standard) characters for icon descriptions, up from 120. (Emoji and other high-unicode characters may use extra space; it's 600 bytes.)

Issue 2061: better error message for link to private list while logged out (pull request)
Category: unfriendly error messages, making things make sense
Patch by: [ profile] rahaeli
Description: There are some places on the site that show super unfriendly error messages. Now, trying to look at a reading filter that you're not authorized to see (because it exists and you're not allowed, because it exists and you're not logged in, or because it doesn't exist) will gently suggest that maybe you should log in if you're sure that actually exists.

Issue 2062: code machete: remnants of Blogger support in cgi-bin/Apache/ (pull request)
Category: code machete
Patch by: [ profile] kareila
Description: Once upon a time, LiveJournal did something special with Blogger. That time is no longer now.

Issue 2065: Shop: Country list contains UK twice (pull request)
Category: making things make sense
Patch by: [ profile] kareila
Description: Two years ago, someone spotted that the UK was listed twice in the journal location drop-down menu. That was due to Dreamwidth having switched the country list from Source A to Source B, and some yelling at the database later, everything was fixed. Except ... it hadn't been fixed in the shop, which also uses a country list, which we'd switched from Source A to Source B...

Issue 2069: allow DW as an import source for dev servers (pull request)
Category: development, importer
Patch by: [ profile] kareila
Description: Testing can be hard. Generating test data can be hard. This way, developers can use their own journals as a source of test data, a lot more easily.

Issue 2071: Use https for CC license image on /legal/diversity (pull request) (nonfree pull request)
Category: HTTPS is fun for the whole family
Patch by: [ profile] kareila
Description: In the grander scheme of things, that skeezy hacker on the unsecured wifi in the coffee shop knowing that you visit a website that's got creative commons licensing on something is probably not the end of the world. But it's not good for people to get in the habit of ignoring mixed content warnings, which is what you get when you're viewing a mix of HTTPS (the conversation is encrypted) and HTTP (the conversation is not encrypted).

Issue 2072: Logged-out /login and logged in or out /openid/ give 'not fully secure' warning (pull request)
Category: HTTPS for the people
Patch by: [ profile] kareila
Description: There were more little places on the site using HTTP where they could and should be using HTTPS instead.

Issue 2073: Use HTTPS for 'some' rights reserved in site footer
Category: HTTPS for the people
Patch by: [ profile] rahaeli
Description: Hard-coded bits with http:// links instead of https:// links are like sand in your hair after visiting the beach.

Issue 2074: Use HTTPS for Zazzle link in Shop nav menu (pull request) (nonfree pull request)
Category: HTTPS for the people
Patch by: [ profile] kareila
Description: Maybe it's glitter. Hard-coded http:// links instead of https:// are like glitter. It just keeps showing up.

Issue 2075: Code machete marathon (pull request)
Category: code machete, burninating the countryside
Patch by: [ profile] kareila
Description: [staff profile] karzilla continues to burninate unneeded bits of code.

Issue 2077: allow /support/submit to have URL argument for supportcat (pull request)
Category: support, making things make sense
Patch by: [ profile] kareila
Description: Dreamwidth's technical support board is a fascinating (and sometimes frustrating) little home-built system that dates back to the LiveJournal days. This change will make it easier for [staff profile] denise to say "This is a little too complicated for me to answer here and now in this comment" and have the support request go to the right place quickly. (Getting things in the wrong place mostly means that someone has to see them and move them, which is just a little delay and not the end of the world. Please don't stress about putting something in the wrong place!)

Issue 2078: admin tool to look up OpenID account by URL (pull request)
Category: admin tools, OpenID users are people too
Patch by: [ profile] kareila
Description: This is one of the sorts of admin tools that everyone thinks must exist, but didn't.

Issue 2079: get rid of "Why are invite codes sometimes required to create a free account?" FAQ
Category: invite codes, making things make sense
Patch by: [ profile] rahaeli
Description: It's been a very long time since invite codes were necessary to create a free account. Unless things get really weird, it's not likely that they'll be coming back, since they were mostly a tool to control growth at the very beginning.

Issue 2081: importer history admin tool should include import source (pull request)
Category: importer, admin tools
Patch by: [ profile] kareila
Description: Again with the import source, this time in the admin-facing tool that [staff profile] denise looks at when trying to figure out what's gone wrong with another weird import. (Imports Just Work a lot of the time! It's just that when they go wrong, they can go wrong in really weird ways.)

Issue 2086: Support for e-mail TLDs needs expanding (pull request)
Category: valid email addresses are valid
Patch by: [ profile] kareila
Description: You could be the lucky person who coughs up the money to get the email address -- and more to the point, Dreamwidth won't reject that email address out of hand as being an obvious fake. Because, in this brave new world, .pizza is a valid top-level domain.

Issue 2089: URL link to remote entry not correct in some importer notifications (pull request)
Category: importer, making things make sense, the inbox is a weird place
Patch by: [ profile] kareila
Description: Sometimes when you import things, there's a problem with one of your entries. (This one time in Open Beta, the importer thought one of my entries was an email, not an HTML page, and it died horribly.) The importer tries to give you a link to the entry, so you can go look at it and see if there's anything obvious and fixable. Unfortunately, the inbox was cheerfully taking that link and mangling it a bit. It turns out that it was using a "relative address", the kind where everything assumes you're starting from literally the same (internet) page, and gives the location in terms of "Okay, hang a right at this directory, then go until you see something named metaquotes, then it's going to be #821 on the left!" For the original entry for an importer problem, those directions are not going to be useful. (If you leave off the http:// or https:// from a link, that tends to break things a little. Oh, computers.) This will make those error links make much more sense.

Issue 2091: add file hosting quota utilization to see_request (pull request)
Category: support, images!!!
Patch by: [ profile] kareila
Description: 500mb is a generous amount of room for posting the occasional baby picture to Dreamwidth. 500mb gets eaten up very quickly if you are, say, a fairly active Tumblr user who uses IFTTT to cross-post the first picture of that gorgeous gifset to Dreamwidth. And what happens when you go over your 500mb limit (enforced starting this February), you ask? Why, you stop being able to upload new pictures until you remove some of the older ones.
This change will give trusted Support folks a quick look at how much file hosting quota has been used when someone puts in a support request, so if the problem is "Hey, my Tumblr crossposts stopped having pictures???" the answer may be "Alas, file hosting quota," and the Support volunteers won't have to track down [staff profile] mark and tug on his sleeve to get answers about that.
(Showing you how much space you've used is also in the works, but is a separate ticket.)

Issue 2095: remove as xpost destination (while keeping it in the user tag) (pull request)
Category: crossposter
Patch by: [ profile] kareila
Description: (LJR) is another site running a (very old) version of the LiveJournal code. Generally this means that you can still crosspost to it. Recently, their client protocol broke somehow, which broke crossposting. We don't know exactly what they did or when/if it'll get fixed. For now, crossposting is paused, in the hope that people won't be upset and confused when they try to crosspost and get errors instead of actual crossposts. Good luck, LJR! We hope your repairs go smoothly.

Issue 2096: don't return hidden FAQs when someone uses the FAQ search (pull request)
Category: Miscellaneous FAQery
Patch by: [ profile] kareila
Description: Writers (including documentation folks) sometimes have an aversion to deleting perfectly good words. The words may not be appropriate for the situation, but who knows! They might be useful later! Obsolete FAQs are not deleted, but hidden from view. While the user who is casually browsing won't stumble upon them, someone using the search feature might have. This removes those hidden (obsolete, not yet live, or otherwise wrong) FAQs from the search as well.

Issue 2097: make feed-fetch user agent use the same size limits as synsuck (pull request)
Category: Syndication, making things make sense
Patch by: [ profile] kareila
Description: is pretty cool. You can read public stuff from other sites right on your reading page. (I would never be able to keep track of what my youngest is doing on Tumblr, for example, without having her Tumblr activity on my reading page as a feed.) Recently there was a little bit of a to-do when a perfectly good source refused to create a feed account, for no apparent reason. It turned out that Dreamwidth was still using very old settings.
[staff profile] mark: "This was written in a time when web servers have 4GB of RAM and such."
In those days, 0.3mb (300kb for those who still remember dial-up file sizes) was alarmingly large. These days, a 6mb limit is more reasonable. So it is written, and after the code push, so it shall be done.

Issue 2105: add Facebook and Instagram to external user tag formats (pull request)
Category: external sites, profile
Patch by: [ profile] kareila
Description: Facebook, and Instagram (which Facebook now owns, and dangles out like an anglerfish's lure), are sites that exist on the internet and people use. You will soon be able to add your presence there to your profile (if you wish), and mention users on those sites as follows:
<user name="whoever" site="">, <user name="whoever" site="facebook">, <user name="whoever" site="fb">
<user name="whoeverelse" site="">, and <user name="whoeverelse" site="instagram">.

Note: Facebook requires and enforces "authentic names", which while not exactly antithetical to the spirit of privacy and personally controlled disclosure that Dreamwidth tries to embody, it's not always compatible. If you have been entrusted with both someone's Dreamwidth identity and their Facebook identity, be aware that these items might need to be kept separate. Unless someone else has made a public connection between their Dreamwidth identity and their Facebook identity, err on the side of not making that connection for them.

Issue 2112: remove code that assumes Unicode support is optional (pull request)
Category: back-end, unicode is here to stay
Patch by: [ profile] kareila
Description: Unicode is an international standard for text and other characters. Back when LiveJournal was built, people were kind of side-eying it and going "Yeah, but..." Since this is 2017 CE (AM 5777, 1438 AH, 3183 YOLD), it's time to embrace Unicode with the kind of tentacular love that will be hell to unglue if another standard supersedes it.

Issue 2115: move /openid/server to DW::Controller (pull request)
Category: OpenID, back-end
Patch by: [ profile] kareila
Description: Bringing Dreamwidth's OpenID stuff into the present, and getting rid of stuff that only made sense on LiveJournal.

Issue 2118: Cleanup 2017q2 (pull request)
Category: back-end, making things make sense, code machete, removing BML
Patch by: [ profile] kareila
Description: A potpourii of tiny things that [staff profile] karzilla has fixed, including but not limited to scraping off references to "Brad's Markup Language" pages that were de-Brad-ified years ago.
[ profile] rahaeli: Quoth Sarah: "The 1990s called, but they couldn't get through because the dial-up modem was using the phone line."

Issue 2119: Some Support requests sometimes incorrectly marked as still needing help
Category: Support, weird back-end magic
Patch by: [ profile] zorkian, we think
Description: Occasionally a technical support request got re-opened for ... no reason that anyone could figure out (that is to say, it wasn't the user re-opening it, it just re-opened on its own). We think this other thing that Mark did will also keep that from happening again.

Issue 2121: BML::get_query_string not preserved when doing an SSL redirect?
Category: HTTPS for the people; removing BML; bugs that are old enough to drive unsupervised in Idaho, Montana, North Dakota, and South Dakota
Patch by: [ profile] zorkian
Description: There was a thing that was working okay for years, but Brad's Markup Language decided to treat HTTPS:// different from HTTP://. Goodbye, BML file. We will not miss you.

Issue 2122: Fix redirect for query strings (pull request)
Category: weird back-end magic
Patch by: [ profile] zorkian
Description: This is the other thing that Mark did. The code was trying to be a little too smart for its own good. This makes it relax and stop blocking valid stuff that it wasn't expecting to see.

Issue 2124: fix typo in diversity statement (pull request) (nonfree pull request)
Category: typo patrol!
Patch by: [ profile] NightFlyer
Description: When the root word has the letter 'e' at the end, that letter may not actually persist if other endings have been slapped on. Judgment. Or, in the diversity statement, undesirables.
A hearty welcome to [ profile] NightFlyer, who decided to jump in and help out. A round of champagne (and/or Soylent 1.7, or other celebratory beverage of choice) in honor! 🍾πŸ₯‚πŸ₯€ (that last is in the Emoji 5.0 package, which may not have arrived in all browsers yet).

Issue 2128: Can't remove purged user who Subscribes to you" and/or "Grants you access"
Category: weird cases
Patch by: n/a
Description: Sometimes somebody you didn't know from the dawn of Dreamwidth subscribed to you, and then quietly deleted their journal without bothering to un-add anyone. If having those users display on your profile bothers you (or there's one specific user you'd like to not see there, deleted or not), banning them using the admin console will hide them from your profile.

Issue 2131: alias /editicons to /manage/icons (pull request)
Category: making things make sense, icons
Patch by: [ profile] kareila
Description: Who has time to remember that it's when it ought to be, in order to be consistent with all the other things? Once this goes live, if you have links to /editicons, start updating them to /manage/icons, because eventually (years from now), /editicons will be going away.

Issue 2134: HTML cleaner should replace space in URLs with %20 before calculating checksum (pull request)
Category: making things make sense, HTTPS for the people
Patch by: [ profile] kareila
Description: http:// images with spaces in the file name were confusing some of the back-end tools. This forces the tool to convert the spaces into the web-safe "%20" before doing its magic, thus un-confusing it.

Issue 2136: add imzy to the user tag
Category: external sites, profile
Patch by: [ profile] kareila
Description: If you're an Imzy user, know some Imzy users, or just like their cute little green dragon icon, you're in luck. <user name="someimzyuser" site=""> and <user name="someimzyuser" site="imzy"> will start working shortly, and you can put it on your profile.

Issue 2142: increment dw.opml.used stat when generating output (pull request)
Category: syndication, obscure tools
Patch by: [ profile] kareila
[ profile] zorkian: "I've forgotten all about whatever this is:"
[ profile] rahaeli: "I think it's a way to export the accounts you read to a feed reader?"
[ profile] zorkian: "okay. do we need it? is it even linked anywhere?"
[ profile] rahaeli: "looks like some people use it in third-party tools? idk"
[ profile] rahaeli: "we should stick a tally counter in the module that generates it and see how many times the code is called over the next month or whatever"
[ profile] zorkian: "@rahaeli and I stand around discussing and @kareila picks up the keyboard and gets things done. <3"
[ profile] rahaeli: "I'm still not 100% convinced she isn't telepathic."

Issue 2150: Commit everybody (pull request)
Category: The Most Interesting Man in the World, things that are live on production
Patch by: [ profile] zorkian
Description: Sometimes life comes at you fast, and you've got to shove stuff onto production to fix whatever went wrong in the last code push.
This includes:
- a very basic tool for interacting with the new image back-end
- an importer troubleshooting tool for comments
- refinements to the tool that deals with duplicates created by the importer
- refinements to the hand-run importer tool, for those difficult imports
- importer updates, lowering the load on external sites, and changing the handling of userpic imports so a userpic allowance mismatch won't stop the import

And unless someone sneaks in some fixes under the wire, that's it for now!

Post a comment in response:

Identity URL: 
Account name:
If you don't have an account you can create one now.
HTML doesn't work in the subject.


If you are unable to use this captcha for any reason, please contact us by email at

Notice: This account is set to log the IP addresses of everyone who comments.
Links will be displayed as unclickable URLs to help prevent spam.