1
0
Fork 0
forked from OpenNeo/impress
Commit graph

1269 commits

Author SHA1 Message Date
5d577db649 Oops, load the data for the bulk item quantity form on the trades page! 2024-01-21 06:42:24 -08:00
8e3d2b994f Oops, link to the user lists page for the not-in-a-list case 2024-01-21 06:40:20 -08:00
507b346c2c Move item lists bulk management form to an openable dialog in the header 2024-01-21 06:20:32 -08:00
77d88e50a6 Oh right, make the item kind badges translatable!
Also make it an abbr, which means we need to override the default
text-decoration on it
2024-01-21 05:41:55 -08:00
abe2747b93 Oh right, PB is another kind of item! 2024-01-21 05:23:53 -08:00
31468c9682 Replace rarity on item page with NC/NP badge, styled after 2020 2024-01-21 05:20:08 -08:00
e8832f2c36 Move item description into header 2024-01-21 05:07:45 -08:00
e4fb067e45 Remove old trade hangers UI from item page 2024-01-21 04:49:06 -08:00
b9bb697ca1 Add trade counts to item page tab navigation 2024-01-21 04:45:22 -08:00
eb6f196b15 Add tab navigation to get to item trade pages 2024-01-21 04:40:25 -08:00
4b9e11fc2a Sort trades in vaguely-recent order
This logic is copied from DTI 2020! Though I didn't include the part
where we highlight trade matches yet!
2024-01-21 03:59:06 -08:00
bfb11e94e3 Fix style bug in trades page
Oops, I forgot this import! It worked when rebuilding styles in dev,
but not once I reloaded the server.
2024-01-21 03:58:33 -08:00
c6927c2ce8 Add basic styles to trades page
Still not accessible via links, just exists!
2024-01-21 03:38:02 -08:00
402e3d4afb Basic trade hangers page, just content and without style
We're just getting started but there we go!! No links or styles yet,
just getting it done!
2024-01-21 03:10:06 -08:00
a03ae90697 Move more of the trade-fetching logic into the model
It was a bit tricky to figure out the right API for this, since I'm
looking ahead to the possibility of splitting these across multiple
pages with more detail, like we do in DTI 2020.

What I like about this API is that the caller gets to apply, or not
apply, whatever scopes they want to the underlying hanger set (like
`includes` or `order`), without violating the usual syntax by e.g.
passing it as a parameter to a method.
2024-01-21 00:39:20 -08:00
80e158caf7 Remove extra item zones section from item page
This is in the previewer UI now, we can remove this extra one!
2024-01-20 23:59:08 -08:00
b1c1bea7be Remove unused data in items#show controller
Oh right, this was for the old pet previewer. Goodbye!
2024-01-20 23:53:30 -08:00
68578aa929 Fix the two-line case for show more/less
I made two mistakes here! One is that you need to round up subpixel
line heights, and the other is that `clientHeight` is what I want, I
shouldn't include the margin!
2024-01-19 04:35:44 -08:00
4c809a5f5f Put back the show more / show less behavior for item page trades
I guess I deleted this a while ago without really noticing… I think I'd
at some point like to replace this with like, the DTI 2020 improved
table layout thing, but I figured this would be pretty quick to throw
in and make the page not feel like a pain to use lmao
2024-01-19 04:29:43 -08:00
f008dff3f4 Remove duplicates from the list of trades
Oh yeah, a long-standing limitation. Good thing we're better at stuff
now!

This is also probably the real cause of the weird number of slight
discrepancies between main DTI and DTI 2020 when I eyeballed stuff lol

oh, well, that and the missing default-lists. A bit messy!
2024-01-19 01:39:25 -08:00
05c8e7f618 Oops, include default-list hangers in the trade lists
Without the `left_outer_joins`, this query only returns hangers that
have a matching list, which defeats the purpose of two of the three
clauses! lol
2024-01-19 01:27:57 -08:00
ebb1be88a1 Lil shout-out for the new active-users list filtering
Just a little message that's even timebomb-scheduled to disappear!
2024-01-19 01:18:24 -08:00
bec350e9f2 Filter trade lists to active users only
I don't have the cute fancy UI with trade list names included, but! Now
the trade ratios should match DTI 2020 and that's nice!
2024-01-19 00:38:16 -08:00
16328d3840 Refactor how we load closet hangers
Ahh I see, the way we got away with not having a `trading` scope before
was a weird metaprogramming `{owned/wanted}_trading` situation. Okay,
let's trash that in favor of our new stuff! And that helps us bulk the
queries too which is nice.
2024-01-19 00:28:28 -08:00
470c805880 Save last trade activity time onto User
In impress-2020, we do a big slow query to figure out which users have
been active in trades recently. Now, we cache that timestamp on the
User model.

This won't have any immediate effect; it's to clear the way for Classic
DTI to receive the better trade ratios feature people like from 2020.

I also added some unit testing infra because I finally wanted it! for
all the ways you can trigger this timestamp lol

Note too that this is a bit of an unusually complex migration, but my
hope is that the batching and query structure and such helps it run
surprisingly fast! 🤞
2024-01-19 00:00:46 -08:00
b84942d77c Use CSS grid instead of floats for item page info
I'm planning to mess with this layout a bit, I don't want floats
getting in my way!
2024-01-18 22:16:56 -08:00
e8db9cf729 Use CSS grid for item page header
Just modernizing some stuff, hum dee dum
2024-01-18 22:07:30 -08:00
0be9dee6fc Delete unused #item-preview styles from items#show page
Oops, left these lying around after replacing the UI with the 2020 one!
2024-01-18 21:53:00 -08:00
6d343989de Extend OWLS caching from 15 minutes to 1 day
Poking at the site, I didn't love that clicking any of the NC items on
the homepage took a few seconds to load cuz of the OWLS request taking
a couple seconds.

If the request were faster to load I might decide otherwise, but for
now, let's just keep that cache long.

Another approach we could take would be to ask for an endpoint that
returns all the values at once, so we could cache and update them all
together, instead of having it all split into separate cache keys!
2024-01-15 06:19:48 -08:00
e337767954 Fix "couldn't download the associated Flash files" when modeling
So this was a slightly wrong error message, what was happening was:

1. Trying to load the image hash for this pet, by looking them up at
   https://pets.neopets.com/cpn/PET_NAME/1/1.png and seeing what URL it
   redirects to.
2. But pets.neopets.com was rejecting our User-Agent string, which
   would've been just "Ruby", since we hadn't set it otherwise. I guess
   that's an explicitly banned string?

I also found that the kind of more-helpful User-Agent string I like to
write was being rejected, and I could only get it to accept something
very simple? So that's what we're using now, I guess!!
2024-01-15 01:15:35 -08:00
d4e77b9ad9 Remove Pardon Our Dust message and page
I think it's served its purpose! I'll do more updates in another venue
sometime probably!
2024-01-14 23:00:23 -08:00
a656cd511a Create endpoint to show all species appearances for a given color
Building toward replacing more of the 2020 data sources! I think this is
an endpoint that benefits from bulk loading, esp with the way the item
page previews work. I also like taking the concept of "canonical" out of
the GQL interface, and instead just loading for each of the 50 species
and letting the client decide. (And then it can fast-swap between them!)
2023-12-05 18:04:54 -08:00
c8de3dae63 Delete unused PetAttribute model files
Oh right, this powered `Color` and `Species` back before they were
database rows. Now these base classes are unused! Goodbye!
2023-12-05 14:56:34 -08:00
99f5cd717f Move some model mixins to lib/
The models folder is a bit confusingly large, these are more mixins and
kinda clutter it. Push them off into `lib`, I think!

I think they used to be in models mainly because Rails used to handle
`lib` differently with autoloading, and it made for a worse dev
experience. Now it's all the same, though!
2023-12-05 14:55:31 -08:00
af5373c390 Add analytics.openneo.net tags
Self-hosted Plausible instance! I have need of usage numbers again,
after a good few years of just not using it; but I don't want to send
the data to Google, and I enjoy self-hosting things, so here we have it!
2023-12-04 22:44:16 -08:00
29c4e4f3ac Remove mini-survey link
That's honestly a bit longer than I even meant to leave it up, but I've
been sick! Time to close shop and start tallying it all up!
2023-11-20 16:30:20 -08:00
f93944d401 Oops, fix the pet preview on the homepage
Oh right, I forgot we use the `pet_types#show` endpoint there! Welcome
back lol!
2023-11-11 16:12:38 -08:00
75a23dfc57 Delete more unused helpers 2023-11-11 15:55:38 -08:00
fb4e967ea1 Delete unused helper 2023-11-11 15:50:07 -08:00
f9669195ea Remove old feedback UserVoice link 2023-11-11 15:48:05 -08:00
c725a11920 Remove unused NC Mall spider code & fields
We haven't used the mall spider in this app in forever (I guess we even
deleted the code at some point?), but there was some vestigial stuff
left. Goodbye!
2023-11-11 15:45:38 -08:00
94d15f005d Delete a couple unused accessors
At least, I think they are!
2023-11-11 15:42:08 -08:00
644a6acc72 Remove old Image Mode stuff
There was a static page explaining it, which we no longer link to; and
there was an unused field in the User model for who was a beta tester
for it. Goodbye!
2023-11-11 15:21:49 -08:00
5905f4aed1 Delete more unused code
Just some routes and controllers that I think were at various points
serving as API endpoints, but no longer do I think!
2023-11-11 15:04:18 -08:00
aae5c767bb Oops, fix species name in zones tooltip
Oh right, this didn't show up in testing bc I only have a few models in
the local db, but yeah I messed this up a bit!!
2023-11-11 11:57:43 -08:00
b54be0ab5c Load item name from Rails app in item preview page
This is just a silly lil single-field migration!
2023-11-11 10:58:14 -08:00
a31039c4c8 Load item page restricted zones data from Rails app, not impress-2020
Just moving more stuff over! I modernized Item's `as_json` method while
I was here. (Note that I removed the NC/own/want fields, because I
think the only other place this method is still called from is the
quick-add feature on the closet lists page, and I think it doesn't use
these fields to do anything: updating the page is basically a full-page
reload, done sneakily.)
2023-11-11 08:49:19 -08:00
c6cb61ef38 Remove no-op item.thumbnail.secure_url
There was a time when I used an old proxy server to try to fix mixed
content issues, and I eventually removed it but never took the tendrils
out from the code.

We probably _should_ figure out how to secure these URLs! But until
then, we may as well simplify the code.
2023-11-11 08:24:08 -08:00
5e25e0bda6 Load item compatibility data from the Rails app, not impress-2020
Ok progress! We've moved the info about what bodies this fits, and
what zones it occupies, into a Rails API endpoint that we now load at
the same time as the other data!

We'll keep moving more over, too!
2023-11-11 08:15:10 -08:00
fe6264b20a Use an actual body with ID "0", instead of the string "all"
I changed my mind again! At first I wanted to make the special case
clearer, and to be able to more strongly assert that the species is
not null. But now I'm like… eh, there's code that references `body.id`
that has no reason _not_ to work in the all-bodies case… let's just
keep the types more consistent, I think.
2023-11-11 08:13:07 -08:00
eaab0b1922 Oops, return an array in the all-bodies case
I was returning just the body itself, oops! For consistency with the
general return value, it should be a one-element array.
2023-11-11 07:58:50 -08:00
133895b213 Refactor appearances data to use a "body" object
This is more similar to what impress-2020 does, I was working on the
wardrobe-2020 code and took some inspiration!

The body has an ID and a species, or is the string "all".
2023-11-11 07:54:56 -08:00
6c31094c2d Add known_glitches to appearances JSON 2023-11-11 07:21:13 -08:00
010f64264f Replace swf_assets#index with item_appearances#index
Preparing a better endpoint for wardrobe-2020 to use! I deleted the
now-unused swf_assets#index endpoint, and replaced it with an
"appearances" concept that isn't exactly reflected in the database
models but is a _lot_ easier for clients to work with imo.

Note that this was a big part of the motivation for the recent
`manifest_url` work—in this draft, I'm probably gonna have the client
request the manifest, rather than use impress-2020's trick of caching
it in the database! There's a bit of a perf penalty, but I think that's
a simpler starting point, and I have a hunch I'll be able to make up
the perf difference once we have the impress-media-server managing more
of these responsibilities.
2023-11-11 07:14:48 -08:00
3b59823fc4 Fix minor item search error message bug
Oops, I got the variable name wrong here! Before this change,
`species:foo` would crash. Now, it shows the error message correctly.
2023-11-11 07:04:57 -08:00
93511b3d51 Delete unused rake tasks 2023-11-10 18:04:24 -08:00
d35b10c1b8 Oops, fix validation bug for SwfAsset
Oh right, saving an asset that doesn't have a manifest will crash this
validation step!
2023-11-10 16:16:32 -08:00
96998643b5 Add manifest_url to swf_assets table
Ok so, impress-2020 guesses the manifest URL every time based on common
URL patterns. But the right way to do this is to read it from the
modeling data! But also, we don't have a great way to get the modeling
data directly. (Though as I write this, I guess we do have that
auto-modeling trick we use in the DTI 2020 codebase, I wonder if that
could work for this too?)

So anyway, in this change, we update the modeling code to save the
manifest URL, and also the migration includes a big block that attempts
to run impress-2020's manifest-guessing logic for every asset and save
the result!

It's uhh. Not fast. It runs at about 1 asset per second (a lot of these
aren't cache hits), and sometimes stalls out. And we have >600k assets,
so the estimated wall time is uhh. Seven days?

I think there's something we could do here around like, concurrent
execution? Though tbqh with the nature of the slowness being seemingly
about hitting the slow underlying images.neopets.com server, I don't
actually have a lot of faith that concurrency would actually be faster?

I also think it could be sensible to like… extract this from the
migration, and run it as a script to infer missing manifest URLs. That
would be easier to run in chunks and resume if something goes wrong.
Cuz like, I think my reasoning here was that backfilling this data was
part of the migration process… but the thing is, this migration can't
reliably get a manifest for everything (both cuz it depends on an
external service and cuz not everything has one), so it's a perfectly
valid migration to just leave the column as null for all the rows to
start, and fill this in later. I wish I'd written it like that!

But anyway, I'm just running this for now, and taking a break for the
night. Maybe later I'll come around and extract this into a separate
task to just try this on all assets missing manifests instead!
2023-11-09 21:42:51 -08:00
3a963c7d25 Fix old .scoped -> .all Rails upgrade bug
Ahh, I guess I missed these, I think they're maybe not actually used in
the app is why? cuz they're all default values that are overridden at
the actual call sites. But I ran into it when running `Pet.load` in the
console, and yeah let's just fix 'em up!
2023-11-09 21:35:42 -08:00
c9dd0f7376 Remove "Import from pets" page
This hasn't worked for a while, and I don't know an API off the top of
my head to drop in for it. Let's just delete it for now, and revisit it
later if we want to!
2023-11-06 13:06:18 -08:00
e9034fb085 Fix petpage etc import
Dang, I'm really wishing I'd opened this sooner cuz I didn't realize it
would be THIS easy!!

The bug was that the `t` method started taking Ruby keyword params
instead of a hash object for `options`, so the syntax changed.
Womp womp!
2023-11-06 12:59:28 -08:00
562cc33045 Fix closet list petpage export
I uhhh don't know why this ever worked at all lmao. Simple fix, I'd
been avoiding it for a while assuming it was worse lol!
2023-11-06 12:55:03 -08:00
47ea796af4 Fix outfit saving infinite loop in error case
Really don't know why this wasn't a problem with Apollo (or was it??),
but yeah, don't save when there's a save error!! Then we reset the
mutation state when the outfit state changes.
2023-11-06 12:54:23 -08:00
f8bcf5a0de Don't save the outfit while it's already saving
It's weird to be reading this code and be like. was this not always an
issue? Maybe something in Apollo prevented this? Did we use optimistic
UI or something? Idk?

There's still an issue with it infinitely retrying in an error state
though.
2023-11-06 12:38:38 -08:00
4ffd85ade0 Add mini-survey announcement
Just sharing this out to gather info, since this might be coming kinda
soon!

I also moved the announcement higher up in the template, because it
gets broken on the user lists page which uses floats quite a bit for
the site header—and tbh I feel like this is better anyway lol.
2023-11-06 12:12:25 -08:00
2126cefdd4 Add posted-at date to Pardon Our Dust page
Just to like. clarify how up-to-date it is and isn't!
2023-11-06 11:54:56 -08:00
57e262a884 Remove unused initialCacheState code from wardrobe-2020
In the impress-2020 app, we use this to prepopulate certain GraphQL
data into the Apollo cache when SSR'ing a page. We don't do that here,
so, goodbye!
2023-11-03 18:16:57 -07:00
a18ffb22a7 Remove the item page drawer, just link to the item page instead
The wardrobe-2020 app had a cute drawer that embeds the item page, but
honestly I don't think it was that valuable, and especially not when it
means we have to basically maintain two item pages lol. Let's decrease
the surface area!
2023-11-03 16:56:51 -07:00
a2feee2d9b Add support for is_manually_nc
A really really simple change! It works on the item page, the item
index page, item search, the homepage, and the item lists page.

The main reason I avoided this for so long (even before modernizing the
Rails app) was that the ElasticSearch stuff felt like it made it messy?
But now it's pretty simple, and it works in search already cuz I did
that when I implemented item search, so, nice!
2023-11-03 16:27:39 -07:00
5dcb1dedb4 Add Owls values to the item page
Eyy it's time!! Long-requested, finally here lol
2023-11-03 16:20:02 -07:00
494f82601f Set up eslint for wardrobe-2020
Ok cool, I have just not been running any of this since moving out of
impress-2020, but now that we're doing serious JS work in here it's time
to turn it back on!!

1. Install eslint and the plugins we use
2. Set up a `yarn lint` command
3. Set up a git hook via husky to lint on pre-commit
4. Fix/disable all the lint errors!
2023-11-02 18:11:07 -07:00
629706a182 Use the main app for outfit deletion, too 2023-11-02 17:39:26 -07:00
d32c6459b0 Normalize outfit data as we load it into wardrobe-2020
Rather than letting the fact that the server API models outfits a bit
differently (underscore keys, integer IDs for things), I'd rather
convert it to the familiar field names and expected types!
2023-11-02 17:12:59 -07:00
7a3aa609ba Use the main app for outfit saving, not impress-2020
This came in a few parts!
1. Add meta tags to let us know we're logged in.
2. Install React Query, which has the data-loading sensibilities I like
   about Apollo without the GraphQL that has honestly been a drag.
3. Replace the outfit-loading and outfit-saving calls with API calls to
   the main app.
4. Update the main app's API calls to use our more flexible data
   constructs like "pose".

Would've loved to do this more incrementally, but it's hard to! You
can't split out outfit-loading and outfit-saving, or auth from any of
that, or the state gets all out-of-sorts.

Still, this is a good nugget we've pulled out all-in-all, and one that
people have been asking for! Can maybe look to logged-in item search
soon too, for own/want data?
2023-11-02 16:54:35 -07:00
1ab3f21917 Finally remove unused auth0 code
Moreover, this code is in a bit of a flimsy state anyway: it'll kinda
work if you're logged in at impress-2020.openneo.net, but that wasn't
intentionally engineered, and I'm not sure exactly what circumstances
cause those cookies to send vs not send?

My intent is to clean up to replace all this with login code that
references the main app instead… this'll require swapping out some
endpoints to refer to what the main app has, but I'm hoping that's not
so tricky to do, since the main app does offer basically all of this
functionality already anyway. (That's not to say it's simple, but I
think it's a migration in the right direction!)
2023-11-01 15:26:53 -07:00
7948974949 Do preloading manually on user list pages, to reduce memory usage
I used the new profiler tools on this page, and noticed a lot of
allocations in the Globalize library, which we use for translating
database records. I realized that we were loading all of the fields of
not just all of the items on the page, but all of their translation
records in all locales! We used to scrape data for lots of languages, so
that can be quite a lot!

Unfortunately, Rails's `includes` method to efficiently preload related
records always loads all fields, and simply can't be overridden.

So, in this change we write manual preloading code, to identify the
records we need, load them in big bulk queries, and assign them back to
the appropriate associations. Basically just what `includes` does, but
written out a bit more, to give us the chance to specify SELECT and
WHERE clauses!
2023-10-27 19:42:02 -07:00
c496e33c37 Add mini profiler to each page
It shows up in development always, and if you're logged in as Me
Specifically in production!

I'm using this to poke at memory usage for pages that seem suspicious.
I don't know why our app reliably grows so large in RAM, but my hunch is
that maybe there are some pages that just use a truly large amount to
begin with - and I've learned Ruby doesn't release memory back after
it's GC'd, it just grows the process and keeps the free space to itself
in its own heap!

So I'm just eyeing pages that I know *can* have a lot going on, and
seeing what I find!
2023-10-27 19:38:49 -07:00
3243a0fdd9 Remove unused Javascript utility libraries
Some of these just didn't have call sites anymore; the HTML5 shim still
did, but that URL is literally broken now lmao. Goodbye!
2023-10-25 16:24:50 -07:00
22e3f4240a Update most URLs to use HTTPS
I noticed we didn't have the little lock icon in the browser, and yeah
huh there's a lot of `http://` still floating around! Let's fix that!
2023-10-25 15:22:57 -07:00
ee3ffe8afe Delete unused item proxy class
We used to do this for weird clever caching tricks that I don't think
were actually very effective. We stopped using this a few months ago,
and now I'm finally cleaning up this supporting code!
2023-10-25 12:55:30 -07:00
ff1f3aa68c Delete unused localized_fragment_exist? method 2023-10-25 12:54:38 -07:00
8d2ed67d89 Delete unused SweeperController
Something about fragment expiration I guess!
2023-10-25 12:53:06 -07:00
4cd12f6132 Delete unused FragmentExpiration module 2023-10-25 12:51:16 -07:00
f8cf3fd701 Fix crash when searching for "is:nc"
Huh, Arel can *sometimes* handle just having an attribute stand in as
"X is true" in a condition, but sometimes gets upset about it. I guess
this changed in Rails since we recently wrote this?

Specifically, item search would crash on "is:nc" (but *not* "is:np"),
saying:

```
undefined method `fetch_attribute' for #<struct Arel::Attributes::Attribute relation=#<Arel::Table:0x0000000109a67110 @name="items", @klass=Item(…), @type_caster=#<ActiveRecord::TypeCaster::Map:0x0000000109a66e90 @klass=Item(…)>, @table_alias=nil>, name="is_manually_nc">
```

The traceback was a bit misleading (it happened at the part where we
merge all the scopes together), but that hinted to me that it working
with an attribute in a place where it expected a conditional. So I
converted the attribute in the `is_nc` scope to a conditional, and made
the matching change in `is_np`, and that fixed it! Ok phew!
2023-10-25 12:46:48 -07:00
8034e8689a Oops, fix silly bug on /outfits/new
I just plum forgot to put the title under `- if outfit`, oops!!
2023-10-24 19:10:05 -07:00
f5b45d2dc8 Delete unused roulette feature
It was a cute idea! But it's been broken for a long time now. Goodbye!
2023-10-24 19:05:18 -07:00
c06cea2a3c Remove slightly-broken edit outfit links from user outfits page
The URL anchors were getting like. double-encoded? The `closet[]` part
was encoding as `closet%255B%255D`. Maybe a thing in Rails, where you
need to mark them `html_safe` to insert them in a URL like that?

Well anyway, those URLs are redundant now, I just have it link straight
to the same outfit page as the big link!
2023-10-24 18:05:51 -07:00
5ba711a413 Delete outfits/show page, point to the editor instead
Now, like in DTI 2020, opening an outfit will go straight to the editor.

I'm not 100% on whether this is actually like. the superior behavior?
But I think it's good enough, and it's what the wardrobe-2020 code
expects, so let's just roll with it for now!
2023-10-24 18:02:18 -07:00
3d07d7f360 Use /outfits/1234 URLs in the editor
Ohh I see, I made a mistake converting this from Next.js routing. It's
not that we had a URL search parameter named `outfitId`; it's that if
you were coming from the `/outfits/:outfitId` route, it would use that!

I still haven't gotten the rest of the site to point that route to this
page, but I'll do that in a later change.
2023-10-24 17:29:07 -07:00
a983ac9053 Add compatibility with older-style outfit URLs
Notable things:
- We used to have the parameters in the hash (`#`) part of the URL.
- We used to use the key `outfit=123` instead of `outfitId=123`.

In this change, we add backwards-compatibility for these things, while
still keeping the latest behavior too, with no change to the URLs we
generate!
2023-10-24 16:50:07 -07:00
e300b2d342 Run Prettier on all wardrobe-2020 JS
Looks like the version of Prettier I just installed is v3, whereas our
last run in the impress-2020 repo was with v2. I don't think we had any
special config in that project, I think these are just changes to
Prettier's defaults, and I'm comfortable accepting them! (Mostly seems
like a lot of trailing commas.)
2023-10-24 16:45:49 -07:00
b8a8cb9b20 Stop orphaning hangers when deleting lists
Idk if this used to be different or what, but it looks like the current
behavior is: if you delete a closet list, it'll leave the hangers
present, but Classic DTI would not show them anywhere; but Impress 2020
(until recently) would crash about it.

Now, we use `dependent: :destroy` to delete the hangers when you delete
the list (which I think makes sense, and is different than what I
decided in the past but that's ok, and is what the current behavior
*looks* like to people!), and we add a migration that deletes orphaned
hangers.

The migration also outputs the deleted hangers as JSON, for us to hold
onto in case we made a mistake! I'm also backing up the database in
advance of running this migration, just in case we gotta roll back HARD!
2023-10-24 15:35:21 -07:00
ddfdd5fc11 Add outfit layers info modal to download PNGs etc
This is an important workflow for people doing art stuff, I'm told! They used to use the Classic DTI broken image UI for this, but now that that's uhh Fully Gone, let's add this more explicitly!
2023-10-23 19:05:10 -07:00
c5edd20b30 Add outfit context menu, with Download button
A better affordance than the popup telling people not to do this lol

and I'm planning to maybe add the button for SWF etc info here too!
2023-10-23 19:05:10 -07:00
35aaaeba8e Fix Chakra UI styles in portal elements
Ah right, the CSS reset only applies in the ScopedCSSReset container, which doesn't work for elements portaled out with the <Portal> component (which a LOT of Chakra components use for things like tooltips etc).

Here, we take advantage of <Portal> having a hardcoded classname .chakra-portal, and applying it to them too!
2023-10-23 19:05:10 -07:00
2c98c1c636 Remove the now-unused pets#submit action
This was used by the Neopia server to send us the modeling data it requested out-of-band. But now we do all our modeling requests back in-app again, so we don't need this!
2023-10-23 19:05:10 -07:00
d8b0bf3174 Skip item translations when modeling
Okay, this is a process that idk if it's even been working for a while anyway, I don't think Neopets translates item names anymore?

And it's crashing when I try to model stuff now, so like. yeah ok I'm fine with just skipping this, it's a shame to lose out on potential data going forward but *I think there just isn't data to get anyway*
2023-10-23 19:05:10 -07:00
5f3ce1210a Stop saving local copies of SWFs
I think we used this for both conversion to image, and also for CORS stuff when rendering Flash-based previews… let's trash it, I don't want to be growing our hard drive with files I don't think we use anymore!

If I'm wrong and it turns out we do use them for something, then like. hey I'm sure we'll find out soon enough, and it's very recoverable operation.
2023-10-23 19:05:10 -07:00
2e152735c5 Stop referencing Neopia, just do modeling inline
I hope this doesn't cause problems! But yeah, with Puma doing threading, and maybe switching to Falcon someday to get even better concurrency properties, I feel like this will probably be fine?

And it makes the UX a loootttt better, to be back in the world where all these forms just work, whew.
2023-10-23 19:05:10 -07:00
e00ee08ae7 Revert timeout back to 1
Oh okay, I was misinterpreting the error: it was that our NEOPETS_URL_ORIGIN secret value isn't the real Neopets.com IP address anymore, so amfphp requests were just plain *always* failing in production. Oops!

I've remove that environment variable from our production config, and now modeling is working in the bulk thing!

Also I'm noticing that we're using puma these days, which does good threading stuff. I think there might be merit to switching over to Falcon because of just how async-y our stuff is, but having 5 threads going is honestly probably good enough that I don't need to worry too much about mutual blocking, and could probably just write stuff to get Neopia out of the picture like *right now*. Neat!
2023-10-23 19:05:10 -07:00
b885ff6ac0 Increase timeout on modeling
Okay so… I'm worried about this because of Rails whole single-threaded situation, which doesn't really let it handle blocking on external network requests very well.

Ultimately I think we're gonna have to do a clever thing but idk quite what?

I should look into whether like, puma + the new async stuff can enable Rails to be more tolerable about this, and handle a few requests at once, instead of having to have the Neopia server doing it. (Right now, the Neopia server isn't really doing its job quite right, because it depends on the Rails app being *local* to send stuff to it.)

But for now, let's just extend the timeout, cuz it's basically always getting hit in production—because there's currently no other way to do modeling, oops lol
2023-10-23 19:05:10 -07:00
966a0025e5 Fix SWF downloads in modeling
A lot simpler with latest Ruby! We can drop the whole SSL workaround yaay
2023-10-23 19:05:10 -07:00
468caea4ae Oops, fix typo include -> includes 2023-10-23 19:05:09 -07:00
3398439bae Fix more modeling bugs
Just find_all_by's that I never cleaned up

Oddly enough, I still got a "neopets seems down" message out of this, idk if that's an actual bug or just sluggishness rn
2023-10-23 19:05:09 -07:00
cbad00f32d Fix AMFPHP requests
Okay, right, if we're just using www.neopets.com (like we are for now), it fails on http://www.neopets.com because it triggers a redirect that we don't follow.

So here I 1) change the default to HTTPS, and 2) add HTTPS support to our little RocketAMF lib
2023-10-23 19:05:09 -07:00
a21ae014ef Fix crash when rendering modeling errors
Oh right, you can't return text from a format.json, but text can be json!
2023-10-23 19:05:09 -07:00
1e690d9f6c Fix old find_or_initialize_by methods
I missed this in the Rails upgrades, oops!
2023-10-23 19:05:09 -07:00
c85c3f5b8f Add the Pardon Our Dust page 2023-10-23 19:05:09 -07:00
c4b7bf1929 Delete some unused images 2023-10-23 19:05:09 -07:00
de245f96f3 Delete some unused public/assets files
Just cleaning up a bit! I'm sure there's more to remove, these were just some clear candidates: old wardrobe code, and stuff in `public` that I just fully don't recognize and don't think is doing anything? (We'll find out if something crashes though lol!)
2023-10-23 19:05:09 -07:00
2d63b1f725 Add viewport meta tag to wardrobe page
Oops, this was causing the page to render in a weird zoomed-out way on mobile!

Note that, for most of the site, we intentionally haven't added this tag yet because most of our pages aren't especially responsively-designed; so we _want_ the device's best attempt to work with that, rather than trying to enforce something.
2023-10-23 19:05:09 -07:00
4f5db2c0f0 Remove analytics placeholder code
Ehh, I don't use analytics anyway. If I do later, maybe I'll add our private Plausible code to here or something!
2023-10-23 19:05:09 -07:00
74197a6e9f Upgrade to latest Sass and Sprockets
This required a buncha fixes to how SASS scoping works! Needed to add a bunch of imports for stuff that previously would get read from the global scope by being imported *after* the constants and mixins etc.

There's clearly a lot of refactor opportunity here, but I'm not gonna worry about it!!
2023-10-23 19:05:09 -07:00
15002d19db Remove compass-rails
I wasn't sure what we were actually using it for, turns out it was mostly polyfills for CSS features that are very standard now!

I didn't audit these changes very carefully tbqh because they seemed pretty simple? Fingers crossed!
2023-10-23 19:05:09 -07:00
64e3702d6d Fix typography on wardrobe 2020 page
Add the green body color back in, and add the Delicious font!
2023-10-23 19:05:09 -07:00
9cc45f0988 Add wardrobe-2020 outfit preview to item pages
Eyyy tasty! There were some issues with conflicting styles with the main app, but I think we got it!

Scoping Chakra's CSS reset was a big deal to not accidentally overwrite the app's own styles lol, and we had to solve a specificity problem for that, thanks Aria for the :where tip!! <3
2023-10-23 19:05:08 -07:00
2eb8a7cd60 Move Apollo error message stuff to apolloClient.js 2023-10-23 19:05:08 -07:00
eef8f1349d Use react-router to *set* the page URL too
We never had a specific reason why we didn't use the router for this I don't think? Not that I wrote down anyway. Let's just switch it over and see what happens!

I mainly did this as a misdiagnosis of the page reload problem fixed in c162864, but it seems like a good idea to try out anyway!
2023-10-23 19:05:08 -07:00
b830198feb Oops, fix bad export in ItemPage
This I think is why the page was reloading when you try to item search? The failed import was triggering our "hey maybe this is an old module URL that got deleted" code?
2023-10-23 19:05:08 -07:00
c6e544be70 Render wardrobe-2020 fullscreen
Tada! No more layout!
2023-10-23 19:05:08 -07:00
8d7eabf1e3 Add AppProvider to wardrobe-2020
Hey the app runs now! How exciting! It doesn't run *correctly* but it renders at all!!
2023-10-23 19:05:08 -07:00
aa76fbc933 Try to render wardrobe-2020 at /wardrobe
Doesn't work yet, there's errors in Apollo! I also wonder what node env it's running as, the messages aren't clear here.
2023-10-23 19:05:08 -07:00
2884914cbe Fix createjs loading
Tricky little thing! Directly importing I think doesn't work because it thinks `this` refers to something other than `window`? This fixes it tho!
2023-10-23 19:05:08 -07:00
3c1fcca986 Remove next/router references
Once again, not really tested, but we don't have the same errors as before so!
2023-10-23 19:05:08 -07:00
6a59fa9f02 Replace next/link with physical links
All of these are links out of wardrobe-2020 now! We'll replace the router with react-router, but just for outfit state stuff.
2023-10-23 19:05:08 -07:00
8765d6c3b0 Replace next/image references
To be clear I haven't really tested this very well bc the page isn't like. working. but I'm just churning through the next references!
2023-10-23 19:05:08 -07:00
81b2a2b4a2 Bundle wardrobe-2020 into the app
We add jsbuilding-rails to get esbuild running in the app, and then we copy-paste the files we need from impress-2020 into here!

I stopped at the point where it was building successfully, but it's not running correctly: it's not sure about `process.env` in `next`, and I think the right next step is to delete the NextJS deps altogether and use React Router instead.
2023-10-23 19:05:08 -07:00
0e7bbd526f Clarify the error behavior on AuthUser syncing
I added bangs to signify they're mutative operations, but also the bang on `create!` helps ensure we'll bail if User creation fails for some reason.
2023-10-23 19:05:08 -07:00
82be3bf5f9 Enable password resets
Nice, just turning it on seemed to do all we need for now!

Fair questions to be asked about like, should you be able to look up by username instead of email? But like idk, this feels simpler *and* more solid, to give you feedback on if it's the right email.
2023-10-23 19:05:08 -07:00
33b2953949 Enable basic AuthUser tracking
Simply turning back on the module that tracks sign-ins and IP addresses.
2023-10-23 19:05:08 -07:00
45090b8d1c Login/logout returns you to the same page
In the login case, we save the `return_to` parameter in the session, because login can be a multi-step process.

In the logout case, we just read it directly from the form params.

Note that you *could* end up in a weird scenario where an old return_to value sticks around for a bit? But we have the sense to delete it when we use it on a successful sign-in, and most links to the login page come with a `return_to` param which should reset it. So, you'd have to 1) have started but not finished a sign-in, 2) during the same session, and 3) get to the login page by an unusual means.

Probably fine!
2023-10-23 19:05:08 -07:00
e79428fa28 Add Remember Me to login
This requires a migration, our first migration against the openneo_id database from this app! Fun!
2023-10-23 19:05:08 -07:00
c7e81314eb Can edit username in user settings page
Right yeah, this just works once we add it to the view! People will be pleased about this :3

Also change the title to Settings!
2023-10-23 19:05:08 -07:00
83bbb84382 Use flash[:notice] instead of flash[:success]
This is a bit more standard, and has the bonus of being compatible with Devise, which is using `flash[:notice]` and so its flashes were coming out unstyled, oops!
2023-10-23 19:05:07 -07:00
d65aafdd4c Signup and settings page for OpenNeo ID accounts
Hey nice!!

Note that I removed an account delete button from the settings page. You can still send a DELETE request to the right endpoint to do it, but it's not gonna delete all the associated records, and I wanna think a bit about how to handle that better before exposing that button.
2023-10-23 19:05:07 -07:00
eee097a9f8 Sync AuthUser and User names
Callbacks are handy for this!
2023-10-23 19:05:07 -07:00
75185de957 Create a User when we create an AuthUser
We also update seeds.rb to be up to date! This should make it possible to log in as test/test123 from a fresh database.
2023-10-23 19:05:07 -07:00
621a0bc211 Mark some more relationships as optional
I noticed this was stopping changing your default list visibility bc contact neopets connection can't be empty, so I fixed that!

And then I just decided to scroll through every `belongs_to` relationship and add optional to the ones that jumped out at me lol
2023-10-23 19:05:07 -07:00
07d2519e40 Stop using update_attributes
Idk when this got removed, but it did! Ok goodbye!
2023-10-23 19:05:07 -07:00
83f80facda Can log into OpenNeo ID accounts directly!
A lot of rough edges here (e.g. no styles on the flash messages), but it's working and that's good!!

I tested this by temporarily switching to the production database and logging in as matchu!

Still missing a lot of big features too, like registration, password resets, settings page, etc.
2023-10-23 19:05:07 -07:00
700e26d7df Remove old OpenNeo ID auth code
This removes login/logout/session logic for integrating with OpenNeo ID, replacing them with stubs that just redirect to `/?TODO` when you click login, and helpers that act as if you're not logged in.

This gives us a clean slate to plug in new Devise logic to integrate with the `openneo_id` database directly!
2023-10-23 19:05:07 -07:00
1d5af835a5 Add AuthUser model, connecting to openneo_id db
No user-facing functionality here yet, just configuring the database connection to work with openneo_id records.

This is a first step in integrating Devise stuff into this app instead of connecting with a weird second app.

My basic testing for this was to temporarily connect to production `openneo_id`, and see `AuthUser.first` correctly return a user!
2023-10-23 19:05:07 -07:00
0e4cc80ac8 Remove sanitize_sql_like monkey-patch
I had added this many Rails versions ago during the recent upgrade process, because it was in latest Rails but not in the version of Rails I was using when replacing Elasticsearch with MySQL queries. We can remove it now!
2023-10-23 19:05:07 -07:00
3263a926dc Oops, add species search filter back
lmao I keep forgetting things! note that the negative case of this filter, like the negative case of `fits`, is currently broken because Rails changed the default SQL mode and I didn't notice! We'll need to add a `database.yml` file and set `sql_mode: TRADITIONAL`.
2023-10-23 19:05:07 -07:00
7e922503b5 Upgrade to Rails 7.0.6
Whew! Seems like a pretty clean one? Ran `rails app:upgrade` and stuff, and made some corrections to keyword arguments for `translate` calls. There might be more such problems elsewhere? But that's hard to search for, and we'll have to see.
2023-10-23 19:05:07 -07:00
eb5e9d53cb Oops, fix anonymous outfits
These are some old issues I just never tested for I think
2023-10-23 19:05:07 -07:00
59efb49419 Upgrade to Rails 6.1.7.4
This one was pretty straightforward yaay! Main thing was the change from `render file` to `render template` in a couple places, oh and a thing with complex `order()` clauses.
2023-10-23 19:05:07 -07:00
cfc5c4ef19 Fix stray bugs on minor codepaths
I ran `rails zeitwerk:check`, which eager-loads the app, and it found two problems: `closet_group.rb` doesn't define `ClosetGroup` (cuz it's empty), and I left in a reference to a cache sweeper observer oops. Goodbye!
2023-10-23 19:05:06 -07:00
62fcc9fe00 Fix moving closet hanger to null-list
Rails 5 added new validation on `belongs_to` to ensure the corresponding record exists. In the case of moving to the null list, this shouldn't trigger!

I wish we could flag that specifically `nil` is okay, but other values should be validated? But oh well, this is fine!
2023-10-23 19:05:06 -07:00
e8131f3608 Fix bug with Arel ordering
Ok so weird little situation, usually Arel will accept an attribute as a param to `order()`, but not when it's in a very specific situation of all of the following:

`Item.joins(:translations).includes(:translations).limit(30).order(Item::Translation.arel_table[:name])`

For some reason, it's all like "hey I can't call `to_sql` on an attribute!", but only in the scenario where all 3 of those other things are present. Weird!

Anyway, explicitly saying `.asc` fixes this. Ok!
2023-10-23 19:05:05 -07:00
43835f5a25 Fix missing attribute error on homepage
Whoops, this has just always been wrong, right? I wonder what used to happen. Did it send extra queries???
2023-10-23 19:05:05 -07:00
d97c32b5da Upgrade to Rails 5.2.8.1
Some important little upgrades but mostly straightforward!

Note that there's still a known issue where item searches crash, I was hoping that this was a bug in Rails 4.2 that would be fixed on upgading to 5, but nope, oh well!

Also uhh I just got a bit silly and didn't actually mean to go all the way to 5.2 in one go, I had meant to start at 5.0… but tbh the 5.1 and 5.2 changes seem small, and this seems to be working, so. Yeah ok let's roll!
2023-10-23 19:05:05 -07:00
f80d220a62 Upgrade to Rails 4.2.11.3 and Ruby 2.4.10
Some tricks required here to get the dependencies to work out, but we got it!!

Oh also, we move away from the rbenv in Ubuntu's package manager, because it doesn't support more recent Rubies like 2.4.10.
2023-10-23 19:05:05 -07:00
a15cbeb307 Remove pet state labeling & Neopets gem
This labeling technique hasn't worked in a long time bc it requires being logged in. These days we just manually label them with the 2020 support tools I think!

Clearing out the Neopets gem should help us manage some gem dep conflicts in the 4.2 upgrade too (I think the nokogiri one gets tricky?)
2023-10-23 19:05:05 -07:00
0d4c6ca077 Delete utf8 backfill for old Ruby
Idk if the replacement `require` is necessary exactly, but it's the one-to-one replacement for this lib, so let's start there for now!
2023-10-23 19:05:05 -07:00
a8ee091f98 Delete unused Pledgie files
This was back when we used a third-party campaign tool! Haven't run this code in ages!
2023-10-23 19:05:05 -07:00
c4f3a472ff Remove RightAWS gem
We're not connecting to AWS directly anymore, now that we deleted the SWF conversion stuff, so we can finally clear this out!
2023-10-23 19:05:05 -07:00
Matchu
d0616b6dfd Delete Camo references & Addressable gem
At one point we piloted a "Camo" service to proxy HTTPS image urls for us, but it doesn't exist anymore.

We already have proxies and stuff for this, so I left `Image` as a placeholder for this, but it's not working yet!

This also deletes our final reference to the Addressable gem, so we can remove it!
2023-10-23 19:05:05 -07:00
Matchu
fd263ea82f Remove mall spider cron jobs
I don't think these work anymore, and our volunteers get new items into the db fast anyway, Impress 2020 is doing better spidering these days. And then we get to remove the cron job `whenever` gem!
2023-10-23 19:05:05 -07:00
Matchu
31ebf7d4eb Remove OutfitImageUploader and Carrierwave
Ok right, missed this in the outfit image stuff deletion, get rid of the code and library we were using to push those images up to S3!
2023-10-23 19:05:05 -07:00
Matchu
38f3c9894c Delete WardrobeTip model
lmao I have zero recollection of this, I've been generally trying to avoid too-far-off-the-path cleanup, but this one just seems silly, goodbye
2023-10-23 19:05:05 -07:00
Matchu
93c596007d Clarify image path methods for SwfAsset
Using `s3_path` and stuff made it sound like we were still referencing the original Amazon S3 images - but actually our new asset proxy just uses the same path structure, and we didn't change anything about it.

Oh also I deleted an after_conversion method that isn't used anymore, forgot about that!
2023-10-23 19:05:05 -07:00
Matchu
8ea74b737e Remove outfit image saving
This has already been moved to Impress 2020 too, so we can delete all the image generation and saving!
2023-10-23 19:05:04 -07:00
Matchu
e121d8bba2 Remove SWF conversion
We've already swapped out the backend for this stuff to Impress 2020, so the resque task and the broken image report UI aren't actually relevant anymore. Delete them!

This helps us delete Resque soon too.
2023-10-23 19:05:04 -07:00
Matchu
a4a0188b1b Stop caching trade users on item page
Idk this one might actually be a bit of a pain to load? But I'd want to optimize it differently anyway, and there's overhauls we're already planning to do here.
2023-10-23 19:05:04 -07:00
Matchu
44341ba731 Stop caching pet type images on item page
That's easy queries and easy templates!
2023-10-23 19:05:04 -07:00
Matchu
b87492d4ee Stop caching item page contributors
This lets us remove the contribution observer too!
2023-10-23 19:05:04 -07:00
Matchu
cf2f78703b Stop caching latest contribution
That's another tiny query that I'm okay with just removing the cache overhead complexity for right now!
2023-10-23 19:05:04 -07:00
Matchu
1bf84b5106 Remove unused "outfits#new newest_items" cache
Huh! This cache key seemed to only be referenced in checks and expirations, but was never actually used! So I guess we've been loading the modeling predictions every time for a while huh??

We'll get smarter about that someday, but anyway, that lets us delete our Item resque tasks and ItemObserver!
2023-10-23 19:05:04 -07:00
Matchu
41fdcb5abc Remove newest_items caching from items page
Yeah I'm very unconvinced of the merit of saving us one items/translations query lmao
2023-10-23 19:05:04 -07:00
Matchu
db74dd1e29 Remove as_json item caching
Again I'm just not convinced of the perf on this, and it enables us to delete some whole infra over it, we can improve it another time if it's useful to!
2023-10-23 19:05:04 -07:00
Matchu
ffa73b6b03 Simplify item page rendering
Just removing some caching and the expiration of it! There's still more superfluous(?) caching on the item page to audit, but these seem a bit more sensible about avoiding loading extra data.
2023-10-23 19:05:04 -07:00
Matchu
02abd4e07f Simplify item_link rendering
In the interest of clearing out Resque, I'm just gonna remove a lot of our more complex caching stuff, and we can do a perf pass for things like big item list pages once everything's upgraded. (I'm hopeful that the upgrades themselves improve perf; and if not, that some improved sensibilities 10 years later can find simpler approaches.)
2023-10-23 19:05:04 -07:00
Matchu
44a00146aa Oops, remove some remaining references to Flex
We uninstalled Flex, our Elasticsearch gem, to replace item search with direct DB queries; but I forgot these calls, oops!

I also kinda want to see about deleting the resque tasks altogether, since I'm not sure how to get Resque installed on latest rails bc there seems to be a conflict over the version of Rack? And it'd be nice to get rid of the complexity if we can.
2023-10-23 19:05:04 -07:00
Matchu
5d1cce293b Reimplement Advanced Search
Oh I uhhh flat out forgot about this LMAO

well it's back now!! and was pleasantly easy to build, following the `from_text` example & abstractions
2023-10-23 19:05:04 -07:00
Matchu
3e728a8e56 Oops, fix ambiguous zones_restrict column
When combining occupies and restricts filters, this wouldn't be sure whether to apply to the items or the swf assets.
2023-10-23 19:05:04 -07:00
Matchu
fd87b41c17 Oops, add unowned & unwanted support to search!
Uhhh idk how I messed this up, but right, wanting is not the opposite of owning, LOL!
2023-10-23 19:05:04 -07:00
Matchu
8ba07800d9 Fix item data not loading
Oops, finder options were removed! That's fine, the `:select` on here isn't useful anyway, and I'd rather just load everything and be simpler lol
2023-10-23 19:05:04 -07:00
Matchu
52d4ec8443 Convert has_many conditions to where block
Looks like `conditions` was removed in this version! This broke outfit saving. Now it's fixed!
2023-10-23 19:05:04 -07:00
Matchu
2dd9cfb3d5 Use all instead of scoped
Back in the day, `all` would immediately load up a query into an array, but now I think it's an alias for what `scoped` used to be: a relation that contains everything.
2023-10-23 19:05:04 -07:00
Matchu
7edfda6d19 Rename ClosetList.public -> publicly_visible
This removes a conflict with a new Rails method `.public` on the model!
2023-10-23 19:05:04 -07:00
Matchu
09c9e3f346 Use strong parameters for ClosetHanger 2023-10-23 19:05:04 -07:00
Matchu
0810f6c34b Use strong parameters for ClosetList 2023-10-23 19:05:04 -07:00
Matchu
8aa4aa6e30 Drop NewsPosts model
We never ended up really using this lol, and it has an attr_accessible I want to be rid of so let's just fully trash it while we're at it!
2023-10-23 19:05:04 -07:00
Matchu
248e710fcb Use strong parameters for User 2023-10-23 19:05:04 -07:00
Matchu
523137253c Use strong parameters for Outfit 2023-10-23 19:05:04 -07:00
Matchu
cff393f014 Auto log in as test user in development mode
I want to test some logged-in stuff, but the whole openneo_id app is a mess to integrate with (and I want to eliminate it down the line anyway), so here's a simple hacky thing that just gets you into a test user for development!
2023-10-23 19:05:04 -07:00
Matchu
23b84cba26 Use strong parameters for Donation 2023-10-23 19:05:04 -07:00
Matchu
f9cd563c82 Delete attr_accessible from Campaign
idk what these were even doing here, I never built a UI to edit campaigns?
2023-10-23 19:05:04 -07:00
Matchu
5f887dc8a1 Backfill globalize's find_by_name method
This method was removed in globalize 4, which we upgraded to for compatibility with Rails 4! Let's add back in a similar thing for now!
2023-10-23 19:05:04 -07:00
Matchu
dc9d82ce86 Oops, fix item search crash in wardrobe
Oh right, these aren't the weird item proxy thing anymore, stop calling these methods!
2023-10-23 19:05:03 -07:00
Matchu
0b1e241158 Improve zones.json performance
Idk I'm working on a mobile hotspot rn and not including the translations was making this slow, hey may as well fix while we're here right? lol
2023-10-23 19:05:03 -07:00
Matchu
6581597d7c Add user:owns/wants back to item search
Not so bad, using a condition on `has_many` `through` was a cute trick!!
2023-10-23 19:05:03 -07:00
Matchu
e1b17e05be Add fits and not_fits back to item search
Some fun stuff here to figure out how to API this out well, but I'm pretty pleased with where it ended up!
2023-10-23 19:05:03 -07:00
Matchu
62f1d883af Improve not_occupied solution
Not being a subquery is better! I realized later that a LEFT JOIN would probably do it even betterer? with like `HAVING count(x) = 0`? but the `left_outer_joins` method doesn't seem to be in Rails 4, and I don't want to do stringy joins, so this is fine for now!
2023-10-23 19:05:03 -07:00
Matchu
c581b063c4 Oops, fix not_occupies logic error
Right, previously we were querying "has *at least one asset* that is not in zone X" instead of "has NO assets that are in zone X".

I don't know a fast way to query for that, this will have to do for now!
2023-10-23 19:05:03 -07:00
Matchu
b244057808 Add restricts filter back to item search 2023-10-23 19:05:03 -07:00
Matchu
d952685c5d Add occupies filter back to item search
Mostly adapting what was already there!
2023-10-23 19:05:03 -07:00
Matchu
185b4eb2fc Use arel for item translation joins
Just a bit defensive so we aren't setting up the possibility of an ambiguous query someday!
2023-10-23 19:05:03 -07:00
Matchu
a653b0c20d Add is:pb back to item search 2023-10-23 19:05:03 -07:00
Matchu
66db73748a Simplify filter API a bit
Not doing the tricks with `is_positive` anymore, instead just calling different functions altogether at the call site.

Also, instead of classes, I feel like this is a lot more concise to just write as class methods that create certain instances of a trivial `Filter` data class. Without the tricks of `is_positive` in play, the value of classes goes way down imo.
2023-10-23 19:05:03 -07:00
Matchu
3e3aa6a126 Move Item name search logic to model scope 2023-10-23 19:05:03 -07:00
Matchu
4cd8944bf4 Improve is:X failure message 2023-10-23 19:05:03 -07:00
Matchu
72461972ca Start building new item search
Just name field right now, more to come! A lot deleted lol
2023-10-23 19:05:03 -07:00
Matchu
a29e016555 Update to new scope syntax
Ohh ok, without this change all of our `scope`s were just immediately evaluating the argument and fetching _all_ such matching records immediately, instead of waiting to actually be called. This led to bugs like `pet_type.as_json` returning ALL pet states in the whole db, because the `PetState.emotion_order` scope was being treated as a single predefined query, rather than a query fragment to merge into the current context.

This also explains what happened in 724ed83: that's why things before the scope in the query were being ignored.
2023-10-23 19:05:03 -07:00
Matchu
ed59a874ff Fix SASS @extend of non-existant class
This used to just be ignored, but SASS crashes on it now, reasonable ty!
2023-10-23 19:05:03 -07:00
Matchu
90f799faf8 Remove unused JSX pragma in modeling React code
lol again this is hard to test so uhh I hope this didn't break it all!! though tbh I feel like we removed this feature or something anyway? idk it stopped working in some way
2023-10-23 19:05:03 -07:00
Matchu
2df6ca57cc Fix modeling_i18n_tag crash
Tbh I'm not 100% sure this is a fix, I'm not sure what `haml_concat` was doing here, and the page is still crashing so it's hard to say. But fingers crossed!
2023-10-23 19:05:02 -07:00
Matchu
f0f9033b8f Fix select bug in Item.all_by_ids_or_children
Idk why, but when the `select` was the first thing in the query, it was getting ignored. I wonder if there's something about the `object_assets` scope that I'm not understanding that's overwriting it? Or the `joins`? But whatever, this works, I'm not worried about it for now!
2023-10-23 19:05:02 -07:00
Matchu
14f66b1e9e Fix caching crashes in localized_cache
The controller was like "oh yeah we have that cached" (from previous renders of the app on Rails 3 I think?), but the view disagreed, bc it was appending a template digest to the cache key. That's a smart feature, but not compatible with how we skip queries in the controller, so disable it for now!
2023-10-23 19:05:02 -07:00
Matchu
20c1d3eb5f Update to new set_table_name syntax 2023-10-23 19:05:02 -07:00
Matchu
aa3d28c641 Update syntax for set_inheritance_column 2023-10-23 19:05:02 -07:00
Matchu
b35c773be3 Remove flex from the app (breaks search!)
We'll need to replace the item search query stuff with direct MySQL queries, but that's not ready yet bc the app still isn't booting, so we're committing this in a known broken state for now!
2023-10-23 19:05:02 -07:00
Matchu
7f8f7e624d Remove references to the Stripe gem
Rather than figure out how to upgrade the Stripe gem to be compatible with future Rails, I'd rather just delete the references, since it's currently unused.

I'm not so bold as to go in and fully trash all our donation code; I just want to ensure we're not sending people down broken codepaths, and that if they reach them, the error messages are clear enough.
2023-07-21 18:54:15 -07:00
4f357c2f9c
Use images without awaiting conversion anymore
We set up `impress-asset-images.openneo.net` to redirect to the right asset, without needing to depend on AWS anymore for HTML5-converted items!

Our quick fix for this: always serve `has_image: true` to the frontend, so it always tries to use the image, regardless of whether we've marked it as converted in the database. (We've turned off the converters too!)
2022-10-15 16:26:12 -07:00
Emi Dunn-Rankin
515b089b3b
Update terms link to point to DTI 2020
There's a new terms page over there! Use that instead, and update the lil footer to change the date it was last modified (8 years wow!)
2022-09-25 06:11:34 -07:00
Emi Dunn-Rankin
fe9adb5766
Oops, fix mall spider bug, added by our HTTPS fix
Oh, yeah, shit, okay, when we set `self.url` like that, it's supposed to be the _canonical_ URL for the SWF, not our proxied one—this is the URL that's gonna go in the database.

We do proxying late in the process, like when we're actually setting up to download something, but for just referencing where the asset lives, we use `images.neopets.com`.

In this change, we revert the use of `NEOPETS_IMAGES_URL_ORIGIN`, but we _do_ update this to `https` for good measure. (We currently have both HTTP and HTTPS urls in the database, I guess neopets.com started serving different URLs at some point, this is probably the future! And anything interpreting these URLs will need to handle both cases anyway, unless we do some kind of migration update situation thing.)

We're migrating the incorrect assets with the following query (with the limit changed to match the number we currently see in the DB, just as a safety check):
```
UPDATE swf_assets SET url = REPLACE(url, 'http://images.neopets-asset-proxy.openneo.net', 'https://images.neopets.com') WHERE url LIKE 'http://images.neopets-asset-proxy.openneo.net%' ORDER BY id LIMIT 2000;
```
2022-08-23 03:04:54 -07:00
Emi Dunn-Rankin
bc64164d69
Sigh, fix HTTPS for images AGAIN with a proxy now
Okay, like in the previous commit, we're dealing with forced HTTPS, on a server that isn't going to cooperate with our dependencies' HTTPS version. And this time, I don't think there's a secret origin server that will accept `http://` requests for us.

Thankfully, we have the perfect hack in our back pocket: our own pre-existing images.neopets.com proxy server! I set the following in our secret `.env` file, and now we're good:

```
NEOPETS_IMAGES_URL_ORIGIN=http://images.neopets-asset-proxy.openneo.net
```
2022-08-02 21:17:52 -07:00
Emi Dunn-Rankin
c9117fb318
Merge pull request #3 from openneo:neopets-url-origin
Use secret NEOPETS_URL_ORIGIN to bypass HTTPS
2022-08-02 20:47:34 -07:00
Emi Dunn-Rankin
568a3645de
Use secret NEOPETS_URL_ORIGIN to bypass HTTPS
Oops, neopets.com finally stopped accepting `http://` connections, so our AMFPHP requests stopped working! And our current dependencies make it hard to make modern HTTPS requests :(

Instead, we're doing this quick-fix: we have a connection who knows the internal address for the Neopets origin server behind their CDN, which *does* still accept `http://` requests!

So, when `NEOPETS_URL_ORIGIN` is specified in the secret `.env` file (not committed to the repository), we'll use it instead of `http://www.neopets.com`. However, we still have that in the code as a fallback, just to be a bit less surprising to some theoretical future dev so they can see the real error message, and to self-document a bit of what that value is semantically doing! (The documentation angle is more of why it's there, rather than an actual expectation that any actual person in the future will run the code and get the fallback.)
2022-08-02 20:46:47 -07:00
9540e2122b
Disable HTTPS for SWF asset downloads
Whoops, everything broke because the world is far ahead of us on security! Oh well.
2022-07-23 21:44:17 -07:00
aa75e2496f
Use outfits.openneo-assets.net, bc petpage bug
There's a bug on Neopets.com that breaks links and images for *.openneo.net, on petpages specifically.

So, we've registered a new domain, and we're using that to serve outfit images now.

I'm a bit hesitant to add a new domain name to our like, permanent URL surface area, lol… but I'm not hearing back from TNT, and I already closed the doors on S3, so… here we are, whatever 😅
2021-06-19 09:10:25 -07:00
aceffc56ab
Fix SWF downloading for HTTPS URLs
TNT started using HTTPS URLs! And our old Ruby version (lol 😬) still requires explicit invocation to perform SSL during a request, so requests were failing!

Now, we explicitly build the `Net::HTTPS` object, and turn on `use_ssl` if it's an HTTPS URL! (The shorthand invocation didn't seem to have an option for this, that I could find!)
2021-06-12 02:23:18 -07:00
8d29f50392
Stop saving outfit images, use new URLs everywhere
Here, we turn off the hooks that enqueue outfit image updates, and we disconnect the `OutfitImageUploader` that manages uploaded S3 URLs, instead replacing it with an `image` method that simulates the same basic API.

This should cause _all_ views on Classic DTI to use the new outfit URLs. Some notable examples:
- The user's Outfits page
- The donations page
- The outfit page, and its sharing metadata

I hope I didn't miss anything in the views that will make this crash stuff! I tested the new model code in the Rails console, and checked it against invocations that I noticed when searching the codebase for `outfit.image` 🤞
2021-05-24 20:05:25 -07:00
3e8020cc73
Use impress-outfit-images.openneo.net outfit URLs
Oops, right, I meant to use the new `impress-outfit-images.openneo.net` host for this! It works just fine from `impress-2020.openneo.net` as the backing source right now, but I want these semi-permanent URLs to be a bit more decoupled.
2021-05-20 20:56:21 -07:00
b31a22d4a1
Start serving outfit images via Impress 2020
As part of our project to get off S3 and dramatically reduce costs, we're gonna start serving outfit images that Impress 2020 generates, fronted by Vercel's CDN cache! This should hopefully be just as fast in practice, without requiring an S3 storage cost. (Outfits whose thumbnails are pretty much unused will be evicted from the cache, or never stored in the first place—and regenerated back into the cache on-demand if needed.)

One important note is that the image at the URL will no longer be guaranteed to auto-update to reflect the changes to the outfit, because we're including `updated_at` in the URL for caching. (It also isn't guaranteed to _not_ auto-update, though 😅) Our hope is that people aren't using it for that use case so much! If so, though, we have some ways we could build live URLs without putting too much pressure on image generation, e.g. redirects 🤔

This change does _not_ disable actual outfit generation, because I want to keep that running until we see these new URLs succeed for folks. Gonna wait a bit and see if we get bug reports on them! Then, if all goes well, we'll stop enqueueing outfit image jobs altogether, and maybe wind down some of the infrastructure accordingly.
2021-05-20 20:52:19 -07:00
238a458131
Fix body ID bug saving SwfAsset outside modeling
Oops, if you saved `SwfAsset` outside of modeling code, the `item` field would be empty, and so `item.body_specific?` wouldn't happen.

This would trigger when you even just report a broken image!

Now, we always run the SQL query to check for that flag.
2021-03-16 10:40:01 -07:00
Matt Dunn-Rankin
6697b15413 More generously catch errors on userlookup
Okay so, userlookup stuff hasn't worked in years, because it requires a login now.

But apparently, somewhere recently, the code inside our `neopets` gem started hard crashing, because of assumptions we made about the document we'd get back.

I'm not sure why it only recently started crashing? or if I'm even necessarily right about that?

But anyway, I'm just doing the easiest safest (🤞🏻) change possible: being more generous with the errors we swallow.

Test Plan:
Deploy and cross fingers.
2019-10-27 14:24:21 -07:00
Matt Dunn-Rankin
c7d60e3e41 Add db field to override special color
Okay, fine, finally making this controllable from the db without requiring a deploy :P Setting this new field will cause `item.special_color` to return the corresponding color. This mainly affects what we show on the item page, and what colors we request for modeling on the homepage.
2018-05-09 14:10:20 -07:00
Matt Dunn-Rankin
7707580ed5
Update Neopia URLs to use HTTPS
We recently flipped the switch for various hosts to force HTTPS, yay! This includes `neopia.openneo.net`.

However, I forgot to change the URL scheme in this file. This meant that the form submit from the homepage would go to `http://neopia.openneo.net/`, then redirect to `https://neopia.openneo.net/`, but only preserve the form data in certain browsers. This change should fix that!

Note: This probably breaks the dev environment, where we don't have a cert for `https://neopia.dev.openneo.net`. I'll fix that some other time!
2018-04-16 20:36:50 -07:00
Matt Dunn-Rankin
711aca0008 hardcode more baby IDs :P
Interestingly, these items *are* correctly detecting their special
color on the homepage for model progress. So, we *do* have the ability
to detect this. But I don't have good item data locally, so it would
be hard to test this, so I'm just gonna go with the cheap solution
again, sorry XP
2018-02-25 14:45:13 -08:00
Matt Dunn-Rankin
9771ec834d whoops, accept 1-digit donation amounts! 2018-01-20 17:55:21 -08:00
Matt Dunn-Rankin
55477baa15 new donate layout for post-2017 campaign 2018-01-18 17:14:02 -08:00
Matt Dunn-Rankin
5cae876c46 ugh, one more bug fix? 2017-10-29 15:18:53 -07:00
Matt Dunn-Rankin
80e74da84f whoops, re-add check for explicitly_body_specific?
In bfd825d, we refactored the "is item body-specific?" check. In the process, we dropped the check for the manual override flag, `explicitly_body_specific?`. Not sure if it was an accident or if I was just _so_ confident that it was gonna work :P In any case, re-add the check!
2017-10-29 15:08:22 -07:00
Matt Dunn-Rankin
bd3b75c675 hardcode some color-specific items 😬 2017-10-21 13:32:01 -07:00
Matt Dunn-Rankin
d6ab0e71b3 new JN items URL format 2017-06-17 12:28:07 -07:00
Matt Dunn-Rankin
daa831e2b0 show gear image when no thumbnail URL present
Okay, surprise, the bug was unrelated to Camo config (though I'm glad I cleaned
that up anyway :P). We now, at a low level, serve a placeholder image for item
thumbnail URL if, for some reason, we don't have a good thumbnail URL on hand.
2017-04-01 10:29:12 -07:00
Matt Dunn-Rankin
f54683464f stop appending ?NO_CAMO_CONFIG when image proxying is disabled
One time I did a thing called Camo to try to get our HTTPS pages working,
because images.neopets.com not supporting HTTPS is crazy >_> I've diasbled it
these days, but it had debug behavior to append `?NO_CAMO_CONFIG` to all
proxied URLs when Camo was not configured.

When an item had no thumbnail URL for some reason (mall spider needs fixing,
maybe?), this caused Rails to try to map that empty string into the path
`/assets/?NO_CAMO_CONFIG`, which made Rails complain that it was trying to load
an asset that doesn't exist. This is probably a sign that using `image_tag` for
URLs that *should* be external URLs, but aren't strictly *guaranteed* to be, is
unwise - but, for now, I've just disabled that behavior. I hope Rails has a
better escape hatch for the empty string :P
2017-04-01 10:04:54 -07:00
Matt Dunn-Rankin
512b64a104 update campaign final stretch copy 2017-01-09 11:35:55 -08:00
Matt Dunn-Rankin
17f289f68f whoops, dont trigger autofilter checkbox if we *only* collapsed spaces 2017-01-09 11:16:42 -08:00
Matt Dunn-Rankin
0679d60c82 quote the "fits" filter string if the color/species contains spaces 2017-01-08 12:14:38 -08:00
Matt Dunn-Rankin
dc582e5980 donation error handling 2017-01-07 19:19:44 -08:00
Matt Dunn-Rankin
3b609052d5 donate form visual nits 2017-01-05 20:46:02 -08:00
Matt Dunn-Rankin
c1c2b11a40 ooof, got the amount wrong D: 2017-01-05 18:52:08 -08:00
Matt Dunn-Rankin
6b81da82b1 new donate form :) new colors and new amount chooser 2017-01-05 15:47:12 -08:00
Matt Dunn-Rankin
f5fb8711fc improved donation campaign nav 2017-01-05 13:06:50 -08:00
Matt Dunn-Rankin
6a331bf79b whoops, fix typo in donation fine print 2017-01-05 15:38:17 -05:00
Matchu
b5442d0f3f news posts read from the database, with a campaign-supplement classname option :) 2015-09-26 20:37:03 -07:00
Matchu
49cd93087a get STUCK, actions bar! get. stuck. 2015-09-26 20:09:13 -07:00
Matchu
40f508247d hint at the select all button 2015-09-26 19:59:16 -07:00
Matchu
e4757b0ee4 deselect all 2015-09-26 19:58:31 -07:00
Matchu
e6a2b978f1 oh huh. looks like remove/move are ready. 2015-09-26 19:55:09 -07:00
Matchu
6bba2ae288 bulk action bar renders, but does nothing 2015-09-26 15:31:55 -07:00
Matchu
776b2e864a fun hacks to persist checkboxes after all hangers update 2015-09-26 15:04:59 -07:00
Matchu
48185fd5b8 bulk actions toolbar, with selected items count
TODO: adding an item resets the checked state of everything, boooo.
this is why react is better :'(
2015-09-26 15:00:31 -07:00
Matchu
621c768921 Select All + visual feedback 2015-09-26 14:22:11 -07:00
Matchu
0aca529e27 checkbox for closet management
drop the remove link (just use quantity: 0)
to create a bigger checkbox label target
2015-09-26 13:56:25 -07:00
Matchu
9108703dd0 womp womp, our source link was broken. thanks, dice. 2015-09-26 13:18:58 -07:00
Matchu
b953e572a3 hide desc on complete campaign; add toggle link 2015-09-26 13:17:38 -07:00
Matchu
4ae43f61ea good-enough zeroclipboard implementation 2015-09-24 20:50:27 -07:00
Matchu
bbd11adefa collapse search on empty string 2015-09-24 20:18:15 -07:00
Matchu
c8b9833dee fix the necklace/collar advanced search bug!
Ooh, this one was nasty, and only one symptom ever got noticed:

1. Pick "Occupies: Collar" in Advanced Search.
   You get the text query "occupies:necklace".
2. And, if you try to do "occupies:collar" even in text-based search,
   you *also* get the results from "occupies:necklace" mixed in with
   the correct results.

The trick is that, in Spanish, zone 24 (necklace) is named "collar",
as is zone 27 (collar). Not sure what to do for Spanish, but this
issue also leaked into English: we really don't want English to return
results for Spanish-named zones.

This is a tricky problem, though, because it'd be nice for es users
to be able to type "occupies:hat". I think we'll have to do the quick
fix for now, though, and just only interpret the query in the current
locale.
2015-09-24 19:51:45 -07:00
Matchu
973bbbcb0a even better campaign copy: complete text + ceil to $5 for the finish-up pitch 2015-09-24 19:39:49 -07:00
Matchu
90ac52dc74 tweak the fine print phrasing again ;P 2015-09-22 22:41:00 -07:00
Matchu
2ca784c3a2 thank-you message automatically deploys on campaign success 2015-09-22 22:40:51 -07:00
Matchu
5466cc9301 when remaining costs < $200, pitch harder 2015-09-22 22:19:43 -07:00
d8038f2fbf prefer scraped rainbow pool images over pet images 2015-09-05 18:48:41 +00:00
7b17f70635 show campaign on trade lists
Turns out ~22% of our users initially land on a trade list.
We like to keep the campaign off the pages where space is at a
premium, so we try to whitelist it to major landing pages in order
to avoid accidentally creating a bad experience on some page :)
2015-09-05 17:53:15 +00:00
Matchu
a4079d2e0c campaign 2015 copy updates 2015-08-30 16:49:46 -07:00
Matchu
4a539f32db hope I did this swfimages fix right xD 2015-08-05 20:22:23 -04:00
Matchu
b11d7a8c9c oh dang, did we just fix most of the mixed content? 2015-08-05 20:11:08 -04:00
Matchu
4a18f22571 camo the emotes on the campaign show page 2015-08-05 19:41:42 -04:00
Matchu
5d2c226357 yum, new campaigns 2015-08-05 19:26:12 -04:00
Matchu
6682bf2d3b same-protocol Neopia requests 2015-08-05 18:57:08 -04:00
Matchu
0d88a6dd6f use same-protocol URLs for javascript libraries, so HTTPS is happy 2015-08-05 18:36:28 -04:00
Matchu
eeb00e0ccf Merge branch 'import_gallery' 2015-07-28 15:06:18 -04:00
Matchu
b9a9ce3890 import from gallery
some of the stuff to support single-pageiness feels a bit hacky. ah, well :P
2015-07-28 15:05:40 -04:00
Matchu
954866a3d3 update NeopetsUser TODOs 2015-07-27 19:37:29 -04:00
Matchu
bce2634d2a remove old safety deposit class 2015-07-27 19:36:24 -04:00
Matchu
dfb3aeb9de 404 on bad page type name 2015-07-27 19:36:13 -04:00
Matchu
b9e3d7bff5 ta da, proper importing refactor 2015-07-27 19:32:59 -04:00
Matchu
8a61280320 whoops; Neopia uses ImpressUser, so make sure it can access it 2015-07-27 13:33:15 -04:00
Matchu
deb0aa90f0 refactor importing 2015-07-27 13:25:24 -04:00
Matchu
c5c587fab1 stop crashing when trying to translate item names 2015-07-27 13:23:46 -04:00
Matchu
dcf254a78d finally let people remove all items from the list at once.
I've been doing this manually via email for a long time,
since building new stuff in the logged-in world was a pain in the old env.

But now here we are! Finally, finally :)
2015-07-19 12:35:49 -04:00
Matchu
1070778398 allow pet data submissions from private-block IPs, not just 127.0.0.1 2015-07-18 01:04:53 -04:00
Matchu
8e83adabbd whoops, we broke start from scratch previews when neopia is online xP
(I wonder why nobody noticed ;P)
2015-07-16 22:37:09 -04:00
Matchu
b639453f61 on a fresh copy of the site, hide the latest contribution section 2015-07-16 18:39:43 -04:00
Matchu
e9449b70f2 update impress repository URL :) 2015-07-16 14:21:35 -04:00
093ae27ae8 get upset when we neopets.com bans us :P 2015-07-15 00:09:17 -04:00
169c587e42 1-second timeout on bulk modeling 2015-07-07 12:27:13 -04:00
eb665f10a4 use MobileService for loading the pets for a username 2015-05-28 17:12:39 -05:00
b0cc4c2396 swf links 2015-05-03 16:57:42 -05:00
bf9f9ed82e download links for each image size 2015-05-03 16:37:24 -05:00
caaf524060 download link in left sidebar 2015-05-03 16:01:08 -05:00
9ca68b02b2 parse "fits:8-bit-chomby" as "fits 8-bit chomby" rather than "fits 8 bit"
The "fits:8-bit-chomby" search filter was being read as color=8, species=bit.
Now, we split from the right-hand side of the filter instead.

Still a problem for anyone who explicitly types the Spanish/Portuguese
ordering of "fits:chomby-8-bits", but I'm okay with this cheap fix, since
I bet literally nobody has done that in the past month, if ever :P
2015-04-07 23:13:22 -05:00
0e6f823154 toggle whether a donation campaign is advertised 2015-02-25 13:49:18 -06:00
a11140f9e1 stop breaking on donations that delete their outfit 2015-02-25 13:42:35 -06:00
a3c31f7042 yum, we accept bitcoin! 2014-12-23 23:22:15 -05:00