Commit 4226458fafc0cd64422c77691a2c802d2347ea2d

Authored by miks
2 parents 909c8c34 7cc4b3f6

Merge branch 'master' into project_users_api

Showing 927 changed files with 1017 additions and 799 deletions   Show diff stats
.gitignore
... ... @@ -22,4 +22,4 @@ config/unicorn.rb
22 22 db/data.yml
23 23 .idea
24 24 .DS_Store
25   -
  25 +.chef
... ...
.travis.yml
... ... @@ -9,6 +9,8 @@ branches:
9 9 - 'master'
10 10 rvm:
11 11 - 1.9.3
  12 +services:
  13 + - mysql
12 14 before_script:
13 15 - "cp config/database.yml.$DB config/database.yml"
14 16 - "cp config/gitlab.yml.example config/gitlab.yml"
... ...
Gemfile.lock
... ... @@ -108,7 +108,7 @@ GEM
108 108 bcrypt-ruby (3.0.1)
109 109 blankslate (2.1.2.4)
110 110 bootstrap-sass (2.0.4.0)
111   - builder (3.0.0)
  111 + builder (3.0.2)
112 112 capybara (1.1.2)
113 113 mime-types (>= 1.16)
114 114 nokogiri (>= 1.3.3)
... ... @@ -125,7 +125,7 @@ GEM
125 125 charlock_holmes (0.6.8)
126 126 childprocess (0.3.2)
127 127 ffi (~> 1.0.6)
128   - chosen-rails (0.9.8)
  128 + chosen-rails (0.9.8.3)
129 129 railties (~> 3.0)
130 130 thor (~> 0.14)
131 131 coderay (1.0.6)
... ...
app/assets/images/emoji/+1.png 0 → 100755

5.01 KB

app/assets/images/emoji/-1.png 0 → 100755

4.95 KB

app/assets/images/emoji/100.png 0 → 100755

3.22 KB

app/assets/images/emoji/109.png 0 → 100755

3.61 KB

app/assets/images/emoji/1234.png 0 → 100755

4.64 KB

app/assets/images/emoji/8ball.png 0 → 100755

4.04 KB

app/assets/images/emoji/a.png 0 → 100755

3.08 KB

app/assets/images/emoji/ab.png 0 → 100755

3.77 KB

app/assets/images/emoji/abc.png 0 → 100755

4.15 KB

app/assets/images/emoji/abcd.png 0 → 100755

4.37 KB

app/assets/images/emoji/accept.png 0 → 100755

4.62 KB

app/assets/images/emoji/aerial_tramway.png 0 → 100755

3.41 KB

app/assets/images/emoji/airplane.png 0 → 100755

4.63 KB

app/assets/images/emoji/alarm_clock.png 0 → 100755

6.9 KB

app/assets/images/emoji/alien.png 0 → 100755

5.33 KB

app/assets/images/emoji/ambulance.png 0 → 100755

3.62 KB

app/assets/images/emoji/anchor.png 0 → 100755

4.37 KB

app/assets/images/emoji/angel.png 0 → 100755

6.52 KB

app/assets/images/emoji/anger.png 0 → 100755

3.01 KB

app/assets/images/emoji/angry.png 0 → 100755

4.92 KB

app/assets/images/emoji/ant.png 0 → 100755

2.78 KB

app/assets/images/emoji/apple.png 0 → 100755

5.5 KB

app/assets/images/emoji/aquarius.png 0 → 100755

4.98 KB

app/assets/images/emoji/aries.png 0 → 100755

4.24 KB

app/assets/images/emoji/arrow_backward.png 0 → 100755

3.11 KB

app/assets/images/emoji/arrow_double_down.png 0 → 100755

3.1 KB

app/assets/images/emoji/arrow_double_up.png 0 → 100755

3.53 KB

app/assets/images/emoji/arrow_down.png 0 → 100755

2.94 KB

app/assets/images/emoji/arrow_down_small.png 0 → 100755

2.87 KB

app/assets/images/emoji/arrow_forward.png 0 → 100755

3.13 KB

app/assets/images/emoji/arrow_heading_down.png 0 → 100755

3.44 KB

app/assets/images/emoji/arrow_heading_up.png 0 → 100755

3.44 KB

app/assets/images/emoji/arrow_left.png 0 → 100755

2.97 KB

app/assets/images/emoji/arrow_lower_left.png 0 → 100755

3.26 KB

app/assets/images/emoji/arrow_lower_right.png 0 → 100755

3.26 KB

app/assets/images/emoji/arrow_right.png 0 → 100755

2.95 KB

app/assets/images/emoji/arrow_right_hook.png 0 → 100755

3.63 KB

app/assets/images/emoji/arrow_up.png 0 → 100755

3 KB

app/assets/images/emoji/arrow_up_down.png 0 → 100755

3.46 KB

app/assets/images/emoji/arrow_up_small.png 0 → 100755

3.12 KB

app/assets/images/emoji/arrow_upper_left.png 0 → 100755

3.15 KB

app/assets/images/emoji/arrow_upper_right.png 0 → 100755

3.16 KB

app/assets/images/emoji/arrows_clockwise.png 0 → 100755

1.37 KB

app/assets/images/emoji/arrows_counterclockwise.png 0 → 100755

4.71 KB

app/assets/images/emoji/art.png 0 → 100755

6.59 KB

app/assets/images/emoji/articulated_lorry.png 0 → 100755

2.87 KB

app/assets/images/emoji/astonished.png 0 → 100755

5.9 KB

app/assets/images/emoji/atm.png 0 → 100755

3.98 KB

app/assets/images/emoji/b.png 0 → 100755

2.95 KB

app/assets/images/emoji/baby.png 0 → 100755

5.78 KB

app/assets/images/emoji/baby_bottle.png 0 → 100755

4.36 KB

app/assets/images/emoji/baby_chick.png 0 → 100755

3.87 KB

app/assets/images/emoji/baby_symbol.png 0 → 100755

2.9 KB

app/assets/images/emoji/baggage_claim.png 0 → 100755

3.42 KB

app/assets/images/emoji/balloon.png 0 → 100755

2.25 KB

app/assets/images/emoji/ballot_box_with_check.png 0 → 100755

1.79 KB

app/assets/images/emoji/bamboo.png 0 → 100755

4.56 KB

app/assets/images/emoji/banana.png 0 → 100755

3.82 KB

app/assets/images/emoji/bangbang.png 0 → 100755

1.35 KB

app/assets/images/emoji/bank.png 0 → 100755

5.45 KB

app/assets/images/emoji/bar_chart.png 0 → 100755

2.4 KB

app/assets/images/emoji/barber.png 0 → 100755

4.15 KB

app/assets/images/emoji/baseball.png 0 → 100755

5.89 KB

app/assets/images/emoji/basketball.png 0 → 100755

6.24 KB

app/assets/images/emoji/bath.png 0 → 100755

3.13 KB

app/assets/images/emoji/bathtub.png 0 → 100755

2.72 KB

app/assets/images/emoji/battery.png 0 → 100755

3.72 KB

app/assets/images/emoji/bear.png 0 → 100755

5.43 KB

app/assets/images/emoji/beer.png 0 → 100755

5.95 KB

app/assets/images/emoji/beers.png 0 → 100755

6.44 KB

app/assets/images/emoji/beetle.png 0 → 100755

5.13 KB

app/assets/images/emoji/beginner.png 0 → 100755

2.7 KB

app/assets/images/emoji/bell.png 0 → 100755

4.75 KB

app/assets/images/emoji/bento.png 0 → 100755

5.6 KB

app/assets/images/emoji/bicyclist.png 0 → 100755

6.33 KB

app/assets/images/emoji/bike.png 0 → 100755

4.61 KB

app/assets/images/emoji/bikini.png 0 → 100755

3.8 KB

app/assets/images/emoji/bird.png 0 → 100755

4.76 KB

app/assets/images/emoji/birthday.png 0 → 100755

5.28 KB

app/assets/images/emoji/black_circle.png 0 → 100755

2.31 KB

app/assets/images/emoji/black_joker.png 0 → 100755

3.79 KB

app/assets/images/emoji/black_nib.png 0 → 100755

2.3 KB

app/assets/images/emoji/black_square.png 0 → 100755

1.3 KB

app/assets/images/emoji/blossom.png 0 → 100755

4.13 KB

app/assets/images/emoji/blowfish.png 0 → 100755

3.66 KB

app/assets/images/emoji/blue_book.png 0 → 100755

4.97 KB

app/assets/images/emoji/blue_car.png 0 → 100755

3.99 KB

app/assets/images/emoji/blue_heart.png 0 → 100755

4 KB

app/assets/images/emoji/blush.png 0 → 100755

5.07 KB

app/assets/images/emoji/boar.png 0 → 100755

4.73 KB

app/assets/images/emoji/boat.png 0 → 100755

3.74 KB

app/assets/images/emoji/bomb.png 0 → 100755

5.09 KB

app/assets/images/emoji/book.png 0 → 100755

5.91 KB

app/assets/images/emoji/bookmark.png 0 → 100755

4.59 KB

app/assets/images/emoji/bookmark_tabs.png 0 → 100755

3.09 KB

app/assets/images/emoji/books.png 0 → 100755

6.39 KB

app/assets/images/emoji/boot.png 0 → 100755

3.25 KB

app/assets/images/emoji/bouquet.png 0 → 100755

6.75 KB

app/assets/images/emoji/bow.png 0 → 100755

5.02 KB

app/assets/images/emoji/bowling.png 0 → 100755

4.09 KB

app/assets/images/emoji/bowtie.png 0 → 100755

6.33 KB

app/assets/images/emoji/boy.png 0 → 100755

5.81 KB

app/assets/images/emoji/bread.png 0 → 100755

6.07 KB

app/assets/images/emoji/bride_with_veil.png 0 → 100755

8.32 KB

app/assets/images/emoji/bridge_at_night.png 0 → 100755

5.02 KB

app/assets/images/emoji/briefcase.png 0 → 100755

2.63 KB

app/assets/images/emoji/broken_heart.png 0 → 100755

4.02 KB

app/assets/images/emoji/bug.png 0 → 100755

5.81 KB

app/assets/images/emoji/bulb.png 0 → 100755

4.38 KB

app/assets/images/emoji/bullettrain_front.png 0 → 100755

4.88 KB

app/assets/images/emoji/bullettrain_side.png 0 → 100755

3.75 KB

app/assets/images/emoji/bus.png 0 → 100755

3.97 KB

app/assets/images/emoji/busstop.png 0 → 100755

1.64 KB

app/assets/images/emoji/bust_in_silhouette.png 0 → 100755

1.96 KB

app/assets/images/emoji/busts_in_silhouette.png 0 → 100755

2.95 KB

app/assets/images/emoji/cactus.png 0 → 100755

4.4 KB

app/assets/images/emoji/cake.png 0 → 100755

5.99 KB

app/assets/images/emoji/calendar.png 0 → 100755

2.85 KB

app/assets/images/emoji/calling.png 0 → 100755

3.94 KB

app/assets/images/emoji/camel.png 0 → 100755

4.38 KB

app/assets/images/emoji/camera.png 0 → 100755

4.55 KB

app/assets/images/emoji/cancer.png 0 → 100755

5.26 KB

app/assets/images/emoji/candy.png 0 → 100755

4.4 KB

app/assets/images/emoji/capital_abcd.png 0 → 100755

5.02 KB

app/assets/images/emoji/capricorn.png 0 → 100755

4.56 KB

app/assets/images/emoji/car.png 0 → 100755

4.18 KB

app/assets/images/emoji/card_index.png 0 → 100755

3.66 KB

app/assets/images/emoji/carousel_horse.png 0 → 100755

5.75 KB

app/assets/images/emoji/cat.png 0 → 100755

5.85 KB

app/assets/images/emoji/cat2.png 0 → 100755

5.59 KB

app/assets/images/emoji/cd.png 0 → 100755

6.56 KB

app/assets/images/emoji/chart.png 0 → 100755

4.23 KB

app/assets/images/emoji/chart_with_downwards_trend.png 0 → 100755

2.84 KB

app/assets/images/emoji/chart_with_upwards_trend.png 0 → 100755

2.87 KB

app/assets/images/emoji/checkered_flag.png 0 → 100755

1.64 KB

app/assets/images/emoji/cherries.png 0 → 100755

5.47 KB

app/assets/images/emoji/cherry_blossom.png 0 → 100755

7.01 KB

app/assets/images/emoji/chestnut.png 0 → 100755

5.74 KB

app/assets/images/emoji/chicken.png 0 → 100755

3.89 KB

app/assets/images/emoji/children_crossing.png 0 → 100755

3.38 KB

app/assets/images/emoji/chocolate_bar.png 0 → 100755

5.13 KB

app/assets/images/emoji/christmas_tree.png 0 → 100755

4.61 KB

app/assets/images/emoji/church.png 0 → 100755

4.54 KB

app/assets/images/emoji/cinema.png 0 → 100755

3.49 KB

app/assets/images/emoji/circus_tent.png 0 → 100755

4.57 KB

app/assets/images/emoji/city_sunrise.png 0 → 100755

4.21 KB

app/assets/images/emoji/city_sunset.png 0 → 100755

3.75 KB

app/assets/images/emoji/cl.png 0 → 100755

3.41 KB

app/assets/images/emoji/clap.png 0 → 100755

6.94 KB

app/assets/images/emoji/clapper.png 0 → 100755

4.09 KB

app/assets/images/emoji/clipboard.png 0 → 100755

4.55 KB

app/assets/images/emoji/clock1.png 0 → 100755

2.53 KB

app/assets/images/emoji/clock10.png 0 → 100755

2.53 KB

app/assets/images/emoji/clock1030.png 0 → 100755

2.8 KB

app/assets/images/emoji/clock11.png 0 → 100755

2.53 KB

app/assets/images/emoji/clock1130.png 0 → 100755

2.79 KB

app/assets/images/emoji/clock12.png 0 → 100755

2.45 KB

app/assets/images/emoji/clock1230.png 0 → 100755

2.73 KB

app/assets/images/emoji/clock130.png 0 → 100755

2.78 KB

app/assets/images/emoji/clock2.png 0 → 100755

2.53 KB

app/assets/images/emoji/clock230.png 0 → 100755

2.79 KB

app/assets/images/emoji/clock3.png 0 → 100755

2.43 KB

app/assets/images/emoji/clock330.png 0 → 100755

2.67 KB

app/assets/images/emoji/clock4.png 0 → 100755

2.56 KB

app/assets/images/emoji/clock430.png 0 → 100755

2.78 KB

app/assets/images/emoji/clock5.png 0 → 100755

2.56 KB

app/assets/images/emoji/clock530.png 0 → 100755

2.77 KB

app/assets/images/emoji/clock6.png 0 → 100755

2.52 KB

app/assets/images/emoji/clock630.png 0 → 100755

2.67 KB

app/assets/images/emoji/clock7.png 0 → 100755

2.55 KB

app/assets/images/emoji/clock730.png 0 → 100755

2.75 KB

app/assets/images/emoji/clock8.png 0 → 100755

2.54 KB

app/assets/images/emoji/clock830.png 0 → 100755

2.78 KB

app/assets/images/emoji/clock9.png 0 → 100755

2.43 KB

app/assets/images/emoji/clock930.png 0 → 100755

2.68 KB

app/assets/images/emoji/closed_book.png 0 → 100755

4.73 KB

app/assets/images/emoji/closed_lock_with_key.png 0 → 100755

5.57 KB

app/assets/images/emoji/closed_umbrella.png 0 → 100755

3.78 KB

app/assets/images/emoji/cloud.png 0 → 100755

3.77 KB

app/assets/images/emoji/clubs.png 0 → 100755

1.65 KB

app/assets/images/emoji/cn.png 0 → 100755

3.55 KB

app/assets/images/emoji/cocktail.png 0 → 100755

2.88 KB

app/assets/images/emoji/coffee.png 0 → 100755

4.21 KB

app/assets/images/emoji/cold_sweat.png 0 → 100755

5.83 KB

app/assets/images/emoji/collision.png 0 → 100755

3.69 KB

app/assets/images/emoji/computer.png 0 → 100755

1.67 KB

app/assets/images/emoji/confetti_ball.png 0 → 100755

5.39 KB

app/assets/images/emoji/confounded.png 0 → 100755

5.72 KB

app/assets/images/emoji/congratulations.png 0 → 100755

4.77 KB

app/assets/images/emoji/construction.png 0 → 100755

3.61 KB

app/assets/images/emoji/construction_worker.png 0 → 100755

6.05 KB

app/assets/images/emoji/convenience_store.png 0 → 100755

3.98 KB

app/assets/images/emoji/cookie.png 0 → 100755

7.96 KB

app/assets/images/emoji/cool.png 0 → 100755

4.08 KB

app/assets/images/emoji/cop.png 0 → 100755

6.97 KB

app/assets/images/emoji/copyright.png 0 → 100755

1.54 KB

app/assets/images/emoji/corn.png 0 → 100755

6.54 KB

app/assets/images/emoji/couple.png 0 → 100755

7.44 KB

app/assets/images/emoji/couple_with_heart.png 0 → 100755

7.2 KB

app/assets/images/emoji/couplekiss.png 0 → 100755

7.05 KB

app/assets/images/emoji/cow.png 0 → 100755

5.61 KB

app/assets/images/emoji/cow2.png 0 → 100755

5.18 KB

app/assets/images/emoji/credit_card.png 0 → 100755

2.59 KB

app/assets/images/emoji/crocodile.png 0 → 100755

5.98 KB

app/assets/images/emoji/crossed_flags.png 0 → 100755

3.92 KB

app/assets/images/emoji/crown.png 0 → 100755

5.52 KB

app/assets/images/emoji/cry.png 0 → 100755

5.57 KB

app/assets/images/emoji/crying_cat_face.png 0 → 100755

6.53 KB

app/assets/images/emoji/crystal_ball.png 0 → 100755

6.09 KB

app/assets/images/emoji/cupid.png 0 → 100755

5.29 KB

app/assets/images/emoji/curly_loop.png 0 → 100755

1.23 KB

app/assets/images/emoji/currency_exchange.png 0 → 100755

1.91 KB

app/assets/images/emoji/curry.png 0 → 100755

5.21 KB

app/assets/images/emoji/custard.png 0 → 100755

5.67 KB

app/assets/images/emoji/customs.png 0 → 100755

3.81 KB

app/assets/images/emoji/cyclone.png 0 → 100755

4.78 KB

app/assets/images/emoji/dancer.png 0 → 100755

3.65 KB

app/assets/images/emoji/dancers.png 0 → 100755

7.73 KB

app/assets/images/emoji/dango.png 0 → 100755

4.34 KB

app/assets/images/emoji/dart.png 0 → 100755

5.33 KB

app/assets/images/emoji/dash.png 0 → 100755

5.32 KB

app/assets/images/emoji/date.png 0 → 100755

2.91 KB

app/assets/images/emoji/de.png 0 → 100755

2.58 KB

app/assets/images/emoji/deciduous_tree.png 0 → 100755

7.2 KB

app/assets/images/emoji/department_store.png 0 → 100755

5.04 KB

app/assets/images/emoji/diamond_shape_with_a_dot_inside.png 0 → 100755

5.56 KB

app/assets/images/emoji/diamonds.png 0 → 100755

2.72 KB

app/assets/images/emoji/disappointed.png 0 → 100755

4.65 KB

app/assets/images/emoji/dizzy.png 0 → 100755

2.97 KB

app/assets/images/emoji/dizzy_face.png 0 → 100755

6.13 KB

app/assets/images/emoji/do_not_litter.png 0 → 100755

5.15 KB

app/assets/images/emoji/dog.png 0 → 100755

5.81 KB

app/assets/images/emoji/dog2.png 0 → 100755

5.79 KB

app/assets/images/emoji/dollar.png 0 → 100755

4.51 KB

app/assets/images/emoji/dolls.png 0 → 100755

6.97 KB

app/assets/images/emoji/dolphin.png 0 → 100755

4.24 KB

app/assets/images/emoji/door.png 0 → 100755

3.23 KB

app/assets/images/emoji/doughnut.png 0 → 100755

5.09 KB

app/assets/images/emoji/dragon.png 0 → 100755

7.64 KB

app/assets/images/emoji/dragon_face.png 0 → 100755

6.58 KB

app/assets/images/emoji/dress.png 0 → 100755

3.55 KB

app/assets/images/emoji/dromedary_camel.png 0 → 100755

5.02 KB

app/assets/images/emoji/droplet.png 0 → 100755

3.2 KB

app/assets/images/emoji/dvd.png 0 → 100755

6.83 KB

app/assets/images/emoji/e-mail.png 0 → 100755

2.08 KB

app/assets/images/emoji/ear.png 0 → 100755

4.23 KB

app/assets/images/emoji/ear_of_rice.png 0 → 100755

4.65 KB

app/assets/images/emoji/earth_africa.png 0 → 100755

7 KB

app/assets/images/emoji/earth_americas.png 0 → 100755

6.87 KB

app/assets/images/emoji/earth_asia.png 0 → 100755

7.13 KB

app/assets/images/emoji/egg.png 0 → 100755

5.09 KB

app/assets/images/emoji/eggplant.png 0 → 100755

4.56 KB

app/assets/images/emoji/eight.png 0 → 100755

3.75 KB

app/assets/images/emoji/eight_pointed_black_star.png 0 → 100755

3.21 KB

app/assets/images/emoji/eight_spoked_asterisk.png 0 → 100755

3.92 KB

app/assets/images/emoji/electric_plug.png 0 → 100755

2.75 KB

app/assets/images/emoji/elephant.png 0 → 100755

4.97 KB

app/assets/images/emoji/email.png 0 → 100755

2.63 KB

app/assets/images/emoji/end.png 0 → 100755

1.44 KB

app/assets/images/emoji/envelope.png 0 → 100755

1.62 KB

app/assets/images/emoji/es.png 0 → 100755

4.2 KB

app/assets/images/emoji/euro.png 0 → 100755

3.85 KB

app/assets/images/emoji/european_castle.png 0 → 100755

5.3 KB

app/assets/images/emoji/european_post_office.png 0 → 100755

4.7 KB

app/assets/images/emoji/evergreen_tree.png 0 → 100755

4.81 KB

app/assets/images/emoji/exclamation.png 0 → 100755

1.15 KB

app/assets/images/emoji/eyeglasses.png 0 → 100755

4.81 KB

app/assets/images/emoji/eyes.png 0 → 100755

4.29 KB

app/assets/images/emoji/facepunch.png 0 → 100755

4.76 KB

app/assets/images/emoji/factory.png 0 → 100755

5.43 KB

app/assets/images/emoji/fallen_leaf.png 0 → 100755

4.78 KB

app/assets/images/emoji/family.png 0 → 100755

7.04 KB

app/assets/images/emoji/fast_forward.png 0 → 100755

3.03 KB

app/assets/images/emoji/fax.png 0 → 100755

4.54 KB

app/assets/images/emoji/fearful.png 0 → 100755

5.47 KB

app/assets/images/emoji/feelsgood.png 0 → 100755

1.12 KB

app/assets/images/emoji/feet.png 0 → 100755

1.49 KB

app/assets/images/emoji/ferris_wheel.png 0 → 100755

6.07 KB

app/assets/images/emoji/file_folder.png 0 → 100755

3.92 KB

app/assets/images/emoji/finnadie.png 0 → 100755

1.16 KB

app/assets/images/emoji/fire.png 0 → 100755

3.79 KB

app/assets/images/emoji/fire_engine.png 0 → 100755

4.75 KB

app/assets/images/emoji/fireworks.png 0 → 100755

6.12 KB

app/assets/images/emoji/first_quarter_moon.png 0 → 100755

5.83 KB

app/assets/images/emoji/first_quarter_moon_with_face.png 0 → 100755

4.18 KB

app/assets/images/emoji/fish.png 0 → 100755

4.61 KB

app/assets/images/emoji/fish_cake.png 0 → 100755

5.68 KB

app/assets/images/emoji/fishing_pole_and_fish.png 0 → 100755

4.37 KB

app/assets/images/emoji/fist.png 0 → 100755

5.74 KB

app/assets/images/emoji/five.png 0 → 100755

3.51 KB

app/assets/images/emoji/flags.png 0 → 100755

5.98 KB

app/assets/images/emoji/flashlight.png 0 → 100755

4.91 KB

app/assets/images/emoji/floppy_disk.png 0 → 100755

3.14 KB

app/assets/images/emoji/flower_playing_cards.png 0 → 100755

3.35 KB

app/assets/images/emoji/flushed.png 0 → 100755

5.71 KB

app/assets/images/emoji/foggy.png 0 → 100755

4.51 KB

app/assets/images/emoji/football.png 0 → 100755

6.55 KB

app/assets/images/emoji/fork_and_knife.png 0 → 100755

3.52 KB

app/assets/images/emoji/fountain.png 0 → 100755

4.97 KB

app/assets/images/emoji/four.png 0 → 100755

3.1 KB

app/assets/images/emoji/four_leaf_clover.png 0 → 100755

5.85 KB

app/assets/images/emoji/fr.png 0 → 100755

3.32 KB

app/assets/images/emoji/free.png 0 → 100755

3.52 KB

app/assets/images/emoji/fried_shrimp.png 0 → 100755

7.37 KB

app/assets/images/emoji/fries.png 0 → 100755

6.25 KB

app/assets/images/emoji/frog.png 0 → 100755

4.71 KB

app/assets/images/emoji/fuelpump.png 0 → 100755

4.2 KB

app/assets/images/emoji/full_moon.png 0 → 100755

6.31 KB

app/assets/images/emoji/full_moon_with_face.png 0 → 100755

7.05 KB

app/assets/images/emoji/game_die.png 0 → 100755

2.89 KB

app/assets/images/emoji/gb.png 0 → 100755

5.76 KB

app/assets/images/emoji/gem.png 0 → 100755

4.74 KB

app/assets/images/emoji/gemini.png 0 → 100755

4.2 KB

app/assets/images/emoji/ghost.png 0 → 100755

4.41 KB

app/assets/images/emoji/gift.png 0 → 100755

6.55 KB

app/assets/images/emoji/gift_heart.png 0 → 100755

5.87 KB

app/assets/images/emoji/girl.png 0 → 100755

6.17 KB

app/assets/images/emoji/globe_with_meridians.png 0 → 100755

5.7 KB

app/assets/images/emoji/goat.png 0 → 100755

4.77 KB

app/assets/images/emoji/goberserk.png 0 → 100755

1.3 KB

app/assets/images/emoji/godmode.png 0 → 100755

1.02 KB

app/assets/images/emoji/golf.png 0 → 100755

3.46 KB

app/assets/images/emoji/grapes.png 0 → 100755

5.3 KB

app/assets/images/emoji/green_apple.png 0 → 100755

6.06 KB

app/assets/images/emoji/green_book.png 0 → 100755

4.97 KB

app/assets/images/emoji/green_heart.png 0 → 100755

4.33 KB

app/assets/images/emoji/grey_exclamation.png 0 → 100755

1.12 KB

app/assets/images/emoji/grey_question.png 0 → 100755

1.03 KB

app/assets/images/emoji/grin.png 0 → 100755

5.59 KB

app/assets/images/emoji/guardsman.png 0 → 100755

3.5 KB

app/assets/images/emoji/guitar.png 0 → 100755

4.28 KB

app/assets/images/emoji/gun.png 0 → 100755

3.09 KB

app/assets/images/emoji/haircut.png 0 → 100755

6.93 KB

app/assets/images/emoji/hamburger.png 0 → 100755

5.57 KB

app/assets/images/emoji/hammer.png 0 → 100755

3.58 KB

app/assets/images/emoji/hamster.png 0 → 100755

7.05 KB

app/assets/images/emoji/hand.png 0 → 100755

4.06 KB

app/assets/images/emoji/handbag.png 0 → 100755

5.32 KB

app/assets/images/emoji/hankey.png 0 → 100755

4.64 KB

app/assets/images/emoji/hash.png 0 → 100755

3.65 KB

app/assets/images/emoji/hatched_chick.png 0 → 100755

5.51 KB

app/assets/images/emoji/hatching_chick.png 0 → 100755

5.79 KB

app/assets/images/emoji/headphones.png 0 → 100755

1.87 KB

app/assets/images/emoji/hear_no_evil.png 0 → 100755

6.4 KB

app/assets/images/emoji/heart.png 0 → 100755

3.22 KB

app/assets/images/emoji/heart_decoration.png 0 → 100755

3.42 KB

app/assets/images/emoji/heart_eyes.png 0 → 100755

5.62 KB

app/assets/images/emoji/heart_eyes_cat.png 0 → 100755

6.03 KB

app/assets/images/emoji/heartbeat.png 0 → 100755

3.96 KB

app/assets/images/emoji/heartpulse.png 0 → 100755

6.12 KB

app/assets/images/emoji/hearts.png 0 → 100755

2.86 KB

app/assets/images/emoji/heavy_check_mark.png 0 → 100755

1.08 KB

app/assets/images/emoji/heavy_division_sign.png 0 → 100755

340 Bytes

app/assets/images/emoji/heavy_dollar_sign.png 0 → 100755

1.38 KB

app/assets/images/emoji/heavy_exclamation_mark.png 0 → 100755

1.28 KB

app/assets/images/emoji/heavy_minus_sign.png 0 → 100755

197 Bytes

app/assets/images/emoji/heavy_multiplication_x.png 0 → 100755

591 Bytes

app/assets/images/emoji/heavy_plus_sign.png 0 → 100755

315 Bytes

app/assets/images/emoji/helicopter.png 0 → 100755

4 KB

app/assets/images/emoji/herb.png 0 → 100755

5.75 KB

app/assets/images/emoji/hibiscus.png 0 → 100755

8.13 KB

app/assets/images/emoji/high_brightness.png 0 → 100755

3.96 KB

app/assets/images/emoji/high_heel.png 0 → 100755

4.45 KB

app/assets/images/emoji/hocho.png 0 → 100755

2.45 KB

app/assets/images/emoji/honey_pot.png 0 → 100755

5.69 KB

app/assets/images/emoji/honeybee.png 0 → 100755

5.71 KB

app/assets/images/emoji/horse.png 0 → 100755

4.47 KB

app/assets/images/emoji/horse_racing.png 0 → 100755

5.77 KB

app/assets/images/emoji/hospital.png 0 → 100755

4.77 KB

app/assets/images/emoji/hotel.png 0 → 100755

5 KB

app/assets/images/emoji/hotsprings.png 0 → 100755

3.46 KB

app/assets/images/emoji/hourglass.png 0 → 100755

4.39 KB

app/assets/images/emoji/house.png 0 → 100755

3.43 KB

app/assets/images/emoji/hurtrealbad.png 0 → 100755

1.42 KB

app/assets/images/emoji/ice_cream.png 0 → 100755

5.34 KB

app/assets/images/emoji/icecream.png 0 → 100755

4.5 KB

app/assets/images/emoji/id.png 0 → 100755

3.81 KB

app/assets/images/emoji/ideograph_advantage.png 0 → 100755

3.02 KB

app/assets/images/emoji/imp.png 0 → 100755

6.48 KB

app/assets/images/emoji/inbox_tray.png 0 → 100755

3.61 KB

app/assets/images/emoji/incoming_envelope.png 0 → 100755

2.15 KB

app/assets/images/emoji/information_desk_person.png 0 → 100755

6.45 KB

app/assets/images/emoji/information_source.png 0 → 100755

3.58 KB

app/assets/images/emoji/innocent.png 0 → 100755

6.84 KB

app/assets/images/emoji/interrobang.png 0 → 100755

2.81 KB

app/assets/images/emoji/iphone.png 0 → 100755

3.42 KB

app/assets/images/emoji/it.png 0 → 100755

3.41 KB

app/assets/images/emoji/izakaya_lantern.png 0 → 100755

3.97 KB

app/assets/images/emoji/jack_o_lantern.png 0 → 100755

5.5 KB

app/assets/images/emoji/japan.png 0 → 100755

3.99 KB

app/assets/images/emoji/japanese_castle.png 0 → 100755

4.82 KB

app/assets/images/emoji/japanese_goblin.png 0 → 100755

5.04 KB

app/assets/images/emoji/japanese_ogre.png 0 → 100755

6.98 KB

app/assets/images/emoji/jeans.png 0 → 100755

3.39 KB

app/assets/images/emoji/joy.png 0 → 100755

6.19 KB

app/assets/images/emoji/joy_cat.png 0 → 100755

7.02 KB

app/assets/images/emoji/jp.png 0 → 100755

2.76 KB

app/assets/images/emoji/key.png 0 → 100755

3.37 KB

app/assets/images/emoji/keycap_ten.png 0 → 100755

4 KB

app/assets/images/emoji/kimono.png 0 → 100755

4.82 KB

app/assets/images/emoji/kiss.png 0 → 100755

6.13 KB

app/assets/images/emoji/kissing_cat.png 0 → 100755

6.64 KB

app/assets/images/emoji/kissing_face.png 0 → 100755

5.43 KB

app/assets/images/emoji/kissing_heart.png 0 → 100755

5.63 KB

app/assets/images/emoji/koala.png 0 → 100755

5.55 KB

app/assets/images/emoji/koko.png 0 → 100755

2.79 KB

app/assets/images/emoji/kr.png 0 → 100755

4.99 KB

app/assets/images/emoji/large_blue_circle.png 0 → 100755

4.53 KB

app/assets/images/emoji/large_blue_diamond.png 0 → 100755

3.7 KB

app/assets/images/emoji/large_orange_diamond.png 0 → 100755

3.81 KB

app/assets/images/emoji/last_quarter_moon.png 0 → 100755

6.03 KB

app/assets/images/emoji/last_quarter_moon_with_face.png 0 → 100755

4.23 KB

app/assets/images/emoji/laughing.png 0 → 100755

6.2 KB

app/assets/images/emoji/leaves.png 0 → 100755

5.44 KB

app/assets/images/emoji/ledger.png 0 → 100755

5.78 KB

app/assets/images/emoji/left_luggage.png 0 → 100755

3.93 KB

app/assets/images/emoji/left_right_arrow.png 0 → 100755

3.33 KB

app/assets/images/emoji/leftwards_arrow_with_hook.png 0 → 100755

3.69 KB

app/assets/images/emoji/lemon.png 0 → 100755

5.91 KB

app/assets/images/emoji/leo.png 0 → 100755

4.8 KB

app/assets/images/emoji/leopard.png 0 → 100755

5.26 KB

app/assets/images/emoji/libra.png 0 → 100755

4.13 KB

app/assets/images/emoji/light_rail.png 0 → 100755

3.7 KB

app/assets/images/emoji/link.png 0 → 100755

2.59 KB

app/assets/images/emoji/lips.png 0 → 100755

3.65 KB

app/assets/images/emoji/lipstick.png 0 → 100755

3.3 KB

app/assets/images/emoji/lock.png 0 → 100755

3.59 KB

app/assets/images/emoji/lock_with_ink_pen.png 0 → 100755

4.85 KB

app/assets/images/emoji/lollipop.png 0 → 100755

5.64 KB

app/assets/images/emoji/loop.png 0 → 100755

3.34 KB

app/assets/images/emoji/loudspeaker.png 0 → 100755

5.86 KB

app/assets/images/emoji/love_hotel.png 0 → 100755

5.8 KB

app/assets/images/emoji/love_letter.png 0 → 100755

2.41 KB

app/assets/images/emoji/low_brightness.png 0 → 100755

2.44 KB

app/assets/images/emoji/m.png 0 → 100755

4.63 KB

app/assets/images/emoji/mag.png 0 → 100755

2.97 KB

app/assets/images/emoji/mag_right.png 0 → 100755

3.54 KB

app/assets/images/emoji/mahjong.png 0 → 100755

3.23 KB

app/assets/images/emoji/mailbox.png 0 → 100755

4.1 KB

app/assets/images/emoji/mailbox_closed.png 0 → 100755

4.26 KB

app/assets/images/emoji/mailbox_with_mail.png 0 → 100755

4.47 KB

app/assets/images/emoji/mailbox_with_no_mail.png 0 → 100755

3.03 KB

app/assets/images/emoji/man.png 0 → 100755

5.88 KB

app/assets/images/emoji/man_with_gua_pi_mao.png 0 → 100755

5.2 KB

app/assets/images/emoji/man_with_turban.png 0 → 100755

6.38 KB

app/assets/images/emoji/mans_shoe.png 0 → 100755

4.64 KB

app/assets/images/emoji/maple_leaf.png 0 → 100755

4.35 KB

app/assets/images/emoji/mask.png 0 → 100755

5.11 KB

app/assets/images/emoji/massage.png 0 → 100755

5.89 KB

app/assets/images/emoji/meat_on_bone.png 0 → 100755

5.4 KB

app/assets/images/emoji/mega.png 0 → 100755

4.57 KB

app/assets/images/emoji/melon.png 0 → 100755

8.04 KB

app/assets/images/emoji/memo.png 0 → 100755

4.83 KB

app/assets/images/emoji/mens.png 0 → 100755

3.29 KB

app/assets/images/emoji/metal.png 0 → 100755

3.03 KB

app/assets/images/emoji/metro.png 0 → 100755

3.32 KB

app/assets/images/emoji/microphone.png 0 → 100755

3.59 KB

app/assets/images/emoji/microscope.png 0 → 100755

4.04 KB

app/assets/images/emoji/milky_way.png 0 → 100755

5.74 KB

app/assets/images/emoji/minibus.png 0 → 100755

3.04 KB

app/assets/images/emoji/minidisc.png 0 → 100755

5.46 KB

app/assets/images/emoji/mobile_phone_off.png 0 → 100755

3.44 KB

app/assets/images/emoji/money_with_wings.png 0 → 100755

7.41 KB

app/assets/images/emoji/moneybag.png 0 → 100755

5.37 KB

app/assets/images/emoji/monkey.png 0 → 100755

4.86 KB

app/assets/images/emoji/monkey_face.png 0 → 100755

5.22 KB

app/assets/images/emoji/monorail.png 0 → 100755

4.21 KB

app/assets/images/emoji/moon.png 0 → 100755

3.46 KB

app/assets/images/emoji/mortar_board.png 0 → 100755

4.07 KB

app/assets/images/emoji/mount_fuji.png 0 → 100755

4.89 KB

app/assets/images/emoji/mountain_bicyclist.png 0 → 100755

9.29 KB

app/assets/images/emoji/mountain_cableway.png 0 → 100755

4.3 KB

app/assets/images/emoji/mountain_railway.png 0 → 100755

7.27 KB

app/assets/images/emoji/mouse.png 0 → 100755

6.47 KB

app/assets/images/emoji/mouse2.png 0 → 100755

3.99 KB

app/assets/images/emoji/movie_camera.png 0 → 100755

3.99 KB

app/assets/images/emoji/moyai.png 0 → 100755

2.12 KB

app/assets/images/emoji/muscle.png 0 → 100755

4.56 KB

app/assets/images/emoji/mushroom.png 0 → 100755

4.77 KB

app/assets/images/emoji/musical_keyboard.png 0 → 100755

1.9 KB

app/assets/images/emoji/musical_note.png 0 → 100755

3.11 KB

app/assets/images/emoji/musical_score.png 0 → 100755

1.54 KB

app/assets/images/emoji/mute.png 0 → 100755

6.48 KB

app/assets/images/emoji/nail_care.png 0 → 100755

5.68 KB

app/assets/images/emoji/name_badge.png 0 → 100755

3.89 KB

app/assets/images/emoji/neckbeard.png 0 → 100755

6.27 KB

app/assets/images/emoji/necktie.png 0 → 100755

5.97 KB

app/assets/images/emoji/negative_squared_cross_mark.png 0 → 100755

3.76 KB

app/assets/images/emoji/neutral_face.png 0 → 100755

4.73 KB

app/assets/images/emoji/new.png 0 → 100755

3.83 KB

app/assets/images/emoji/new_moon.png 0 → 100755

5.25 KB

app/assets/images/emoji/new_moon_with_face.png 0 → 100755

6.59 KB

app/assets/images/emoji/newspaper.png 0 → 100755

5.08 KB

app/assets/images/emoji/ng.png 0 → 100755

4.1 KB

app/assets/images/emoji/nine.png 0 → 100755

3.69 KB

app/assets/images/emoji/no_bell.png 0 → 100755

5.8 KB

app/assets/images/emoji/no_bicycles.png 0 → 100755

5.53 KB

app/assets/images/emoji/no_entry.png 0 → 100755

3.43 KB

app/assets/images/emoji/no_entry_sign.png 0 → 100755

3.21 KB

app/assets/images/emoji/no_good.png 0 → 100755

6.87 KB

app/assets/images/emoji/no_mobile_phones.png 0 → 100755

4.96 KB

app/assets/images/emoji/no_mouth.png 0 → 100755

4.62 KB

app/assets/images/emoji/no_pedestrians.png 0 → 100755

5.36 KB

app/assets/images/emoji/no_smoking.png 0 → 100755

4.11 KB

app/assets/images/emoji/non-potable_water.png 0 → 100755

5.08 KB

app/assets/images/emoji/nose.png 0 → 100755

3.62 KB

app/assets/images/emoji/notebook.png 0 → 100755

5.89 KB

app/assets/images/emoji/notebook_with_decorative_cover.png 0 → 100755

5.2 KB

app/assets/images/emoji/notes.png 0 → 100755

1.5 KB

app/assets/images/emoji/nut_and_bolt.png 0 → 100755

2.12 KB

app/assets/images/emoji/o.png 0 → 100755

2.48 KB

app/assets/images/emoji/o2.png 0 → 100755

3.42 KB

app/assets/images/emoji/ocean.png 0 → 100755

5.64 KB

app/assets/images/emoji/octocat.png 0 → 100755

3.84 KB

app/assets/images/emoji/octopus.png 0 → 100755

5.64 KB

app/assets/images/emoji/oden.png 0 → 100755

5.41 KB

app/assets/images/emoji/office.png 0 → 100755

5.04 KB

app/assets/images/emoji/ok.png 0 → 100755

4.06 KB

app/assets/images/emoji/ok_hand.png 0 → 100755

4.49 KB

app/assets/images/emoji/ok_woman.png 0 → 100755

7.35 KB

app/assets/images/emoji/older_man.png 0 → 100755

6.58 KB

app/assets/images/emoji/older_woman.png 0 → 100755

5.84 KB

app/assets/images/emoji/on.png 0 → 100755

1.76 KB

app/assets/images/emoji/oncoming_automobile.png 0 → 100755

7.29 KB

app/assets/images/emoji/oncoming_bus.png 0 → 100755

5.18 KB

app/assets/images/emoji/oncoming_police_car.png 0 → 100755

5.55 KB

app/assets/images/emoji/oncoming_taxi.png 0 → 100755

6.14 KB

app/assets/images/emoji/one.png 0 → 100755

2.76 KB

app/assets/images/emoji/open_file_folder.png 0 → 100755

4.19 KB

app/assets/images/emoji/open_hands.png 0 → 100755

4.83 KB

app/assets/images/emoji/ophiuchus.png 0 → 100755

4.33 KB

app/assets/images/emoji/orange_book.png 0 → 100755

4.97 KB

app/assets/images/emoji/outbox_tray.png 0 → 100755

3.6 KB

app/assets/images/emoji/ox.png 0 → 100755

5.94 KB

app/assets/images/emoji/page_facing_up.png 0 → 100755

2.13 KB

app/assets/images/emoji/page_with_curl.png 0 → 100755

3.64 KB

app/assets/images/emoji/pager.png 0 → 100755

3.93 KB

app/assets/images/emoji/palm_tree.png 0 → 100755

3.58 KB

app/assets/images/emoji/panda_face.png 0 → 100755

4.7 KB

app/assets/images/emoji/paperclip.png 0 → 100755

2.5 KB

app/assets/images/emoji/parking.png 0 → 100755

3.01 KB

app/assets/images/emoji/part_alternation_mark.png 0 → 100755

2.62 KB

app/assets/images/emoji/partly_sunny.png 0 → 100755

5.06 KB

app/assets/images/emoji/passport_control.png 0 → 100755

3.92 KB

app/assets/images/emoji/paw_prints.png 0 → 100755

2.41 KB

app/assets/images/emoji/peach.png 0 → 100755

5.78 KB

app/assets/images/emoji/pear.png 0 → 100755

6.77 KB

app/assets/images/emoji/pencil.png 0 → 100755

4.83 KB

app/assets/images/emoji/pencil2.png 0 → 100755

4.35 KB

app/assets/images/emoji/penguin.png 0 → 100755

4.63 KB

app/assets/images/emoji/pensive.png 0 → 100755

4.94 KB

app/assets/images/emoji/performing_arts.png 0 → 100755

6.14 KB

app/assets/images/emoji/persevere.png 0 → 100755

5.39 KB

app/assets/images/emoji/person_frowning.png 0 → 100755

4.71 KB

app/assets/images/emoji/person_with_blond_hair.png 0 → 100755

6.47 KB

app/assets/images/emoji/person_with_pouting_face.png 0 → 100755

5.3 KB

app/assets/images/emoji/phone.png 0 → 100755

5.37 KB

app/assets/images/emoji/pig.png 0 → 100755

5.86 KB

app/assets/images/emoji/pig2.png 0 → 100755

4.68 KB

app/assets/images/emoji/pig_nose.png 0 → 100755

4.65 KB

app/assets/images/emoji/pill.png 0 → 100755

4.9 KB

app/assets/images/emoji/pineapple.png 0 → 100755

5.5 KB

app/assets/images/emoji/pisces.png 0 → 100755

4.35 KB

app/assets/images/emoji/pizza.png 0 → 100755

5.15 KB

app/assets/images/emoji/plus1.png 0 → 100755

4.96 KB

app/assets/images/emoji/point_down.png 0 → 100755

3.15 KB

app/assets/images/emoji/point_left.png 0 → 100755

3.01 KB

app/assets/images/emoji/point_right.png 0 → 100755

3.01 KB

app/assets/images/emoji/point_up.png 0 → 100755

3.35 KB

app/assets/images/emoji/point_up_2.png 0 → 100755

3.11 KB

app/assets/images/emoji/police_car.png 0 → 100755

3.27 KB

app/assets/images/emoji/poodle.png 0 → 100755

6.69 KB

app/assets/images/emoji/poop.png 0 → 100755

4.64 KB

app/assets/images/emoji/post_office.png 0 → 100755

5.02 KB

app/assets/images/emoji/postal_horn.png 0 → 100755

4.71 KB

app/assets/images/emoji/postbox.png 0 → 100755

3.31 KB

app/assets/images/emoji/potable_water.png 0 → 100755

3.84 KB

app/assets/images/emoji/pouch.png 0 → 100755

4.58 KB

app/assets/images/emoji/poultry_leg.png 0 → 100755

4.1 KB

app/assets/images/emoji/pound.png 0 → 100755

4.14 KB

app/assets/images/emoji/pouting_cat.png 0 → 100755

4.8 KB

app/assets/images/emoji/pray.png 0 → 100755

6.06 KB

app/assets/images/emoji/princess.png 0 → 100755

7.73 KB

app/assets/images/emoji/punch.png 0 → 100755

4.72 KB

app/assets/images/emoji/purple_heart.png 0 → 100755

4.19 KB

app/assets/images/emoji/purse.png 0 → 100755

4.92 KB

app/assets/images/emoji/pushpin.png 0 → 100755

3.7 KB

app/assets/images/emoji/put_litter_in_its_place.png 0 → 100755

4 KB

app/assets/images/emoji/question.png 0 → 100755

1.67 KB

app/assets/images/emoji/rabbit.png 0 → 100755

5.54 KB

app/assets/images/emoji/rabbit2.png 0 → 100755

4.41 KB

app/assets/images/emoji/racehorse.png 0 → 100755

4.62 KB

app/assets/images/emoji/radio.png 0 → 100755

6.01 KB

app/assets/images/emoji/radio_button.png 0 → 100755

2.15 KB

app/assets/images/emoji/rage.png 0 → 100755

5.28 KB

app/assets/images/emoji/rage1.png 0 → 100755

1.06 KB

app/assets/images/emoji/rage2.png 0 → 100755

1.07 KB

app/assets/images/emoji/rage3.png 0 → 100755

1.09 KB

app/assets/images/emoji/rage4.png 0 → 100755

1.24 KB

app/assets/images/emoji/railway_car.png 0 → 100755

3.56 KB

app/assets/images/emoji/rainbow.png 0 → 100755

5.19 KB

app/assets/images/emoji/raised_hand.png 0 → 100755

6.03 KB

app/assets/images/emoji/raised_hands.png 0 → 100755

5.25 KB

app/assets/images/emoji/ram.png 0 → 100755

6.38 KB

app/assets/images/emoji/ramen.png 0 → 100755

6.42 KB

app/assets/images/emoji/rat.png 0 → 100755

5.31 KB

app/assets/images/emoji/recycle.png 0 → 100755

3.62 KB

app/assets/images/emoji/red_car.png 0 → 100755

4.18 KB

app/assets/images/emoji/red_circle.png 0 → 100755

3.85 KB

app/assets/images/emoji/registered.png 0 → 100755

1.58 KB

app/assets/images/emoji/relaxed.png 0 → 100755

5.33 KB

app/assets/images/emoji/relieved.png 0 → 100755

5.52 KB

app/assets/images/emoji/repeat.png 0 → 100755

3.92 KB

app/assets/images/emoji/repeat_one.png 0 → 100755

4.19 KB

app/assets/images/emoji/restroom.png 0 → 100755

4.04 KB

app/assets/images/emoji/revolving_hearts.png 0 → 100755

5.34 KB

app/assets/images/emoji/rewind.png 0 → 100755

2.98 KB

app/assets/images/emoji/ribbon.png 0 → 100755

5.45 KB

app/assets/images/emoji/rice.png 0 → 100755

4.54 KB

app/assets/images/emoji/rice_ball.png 0 → 100755

5.28 KB

app/assets/images/emoji/rice_cracker.png 0 → 100755

7.6 KB

app/assets/images/emoji/rice_scene.png 0 → 100755

6.11 KB

app/assets/images/emoji/ring.png 0 → 100755

5.11 KB

app/assets/images/emoji/rocket.png 0 → 100755

5.26 KB

app/assets/images/emoji/roller_coaster.png 0 → 100755

5.03 KB

app/assets/images/emoji/rooster.png 0 → 100755

6.02 KB

app/assets/images/emoji/rose.png 0 → 100755

4.1 KB

app/assets/images/emoji/rotating_light.png 0 → 100755

6.46 KB

app/assets/images/emoji/round_pushpin.png 0 → 100755

1.89 KB

app/assets/images/emoji/rowboat.png 0 → 100755

5.31 KB

app/assets/images/emoji/ru.png 0 → 100755

3.83 KB

app/assets/images/emoji/rugby_football.png 0 → 100755

7.6 KB

app/assets/images/emoji/runner.png 0 → 100755

3.06 KB

app/assets/images/emoji/running.png 0 → 100755

3.14 KB

app/assets/images/emoji/running_shirt_with_sash.png 0 → 100755

5.57 KB

app/assets/images/emoji/sa.png 0 → 100755

3.47 KB

app/assets/images/emoji/sagittarius.png 0 → 100755

4.4 KB

app/assets/images/emoji/sailboat.png 0 → 100755

3.74 KB

app/assets/images/emoji/sake.png 0 → 100755

4.95 KB

app/assets/images/emoji/sandal.png 0 → 100755

3.88 KB

app/assets/images/emoji/santa.png 0 → 100755

6.12 KB

app/assets/images/emoji/satellite.png 0 → 100755

4.75 KB

app/assets/images/emoji/satisfied.png 0 → 100755

5.24 KB

app/assets/images/emoji/saxophone.png 0 → 100755

4.15 KB

app/assets/images/emoji/school.png 0 → 100755

5.32 KB

app/assets/images/emoji/school_satchel.png 0 → 100755

5.61 KB

app/assets/images/emoji/scissors.png 0 → 100755

3.75 KB

app/assets/images/emoji/scorpius.png 0 → 100755

4.46 KB

app/assets/images/emoji/scream.png 0 → 100755

6.33 KB

app/assets/images/emoji/scream_cat.png 0 → 100755

6.68 KB

app/assets/images/emoji/scroll.png 0 → 100755

6.59 KB

app/assets/images/emoji/seat.png 0 → 100755

5.92 KB

app/assets/images/emoji/secret.png 0 → 100755

5.24 KB

app/assets/images/emoji/see_no_evil.png 0 → 100755

6.67 KB

app/assets/images/emoji/seedling.png 0 → 100755

2.21 KB

app/assets/images/emoji/seven.png 0 → 100755

2.98 KB

app/assets/images/emoji/shaved_ice.png 0 → 100755

5.77 KB

app/assets/images/emoji/sheep.png 0 → 100755

4.62 KB

app/assets/images/emoji/shell.png 0 → 100755

5 KB

app/assets/images/emoji/ship.png 0 → 100755

4.13 KB

app/assets/images/emoji/shipit.png 0 → 100755

9.13 KB

app/assets/images/emoji/shirt.png 0 → 100755

4.57 KB

app/assets/images/emoji/shit.png 0 → 100755

4.64 KB

app/assets/images/emoji/shoe.png 0 → 100755

4.69 KB

app/assets/images/emoji/shower.png 0 → 100755

7.41 KB

app/assets/images/emoji/signal_strength.png 0 → 100755

3.16 KB

app/assets/images/emoji/six.png 0 → 100755

3.7 KB

app/assets/images/emoji/six_pointed_star.png 0 → 100755

4.75 KB

app/assets/images/emoji/ski.png 0 → 100755

4.07 KB

app/assets/images/emoji/skull.png 0 → 100755

2.37 KB

app/assets/images/emoji/sleepy.png 0 → 100755

5.7 KB

app/assets/images/emoji/slot_machine.png 0 → 100755

4.5 KB

app/assets/images/emoji/small_blue_diamond.png 0 → 100755

1.85 KB

app/assets/images/emoji/small_orange_diamond.png 0 → 100755

1.9 KB

app/assets/images/emoji/small_red_triangle.png 0 → 100755

2.01 KB

app/assets/images/emoji/small_red_triangle_down.png 0 → 100755

2.11 KB

app/assets/images/emoji/smile.png 0 → 100755

5.75 KB

app/assets/images/emoji/smile_cat.png 0 → 100755

5.97 KB

app/assets/images/emoji/smiley.png 0 → 100755

5.66 KB

app/assets/images/emoji/smiley_cat.png 0 → 100755

5.94 KB

app/assets/images/emoji/smiling_imp.png 0 → 100755

7.02 KB

app/assets/images/emoji/smirk.png 0 → 100755

5.18 KB

app/assets/images/emoji/smirk_cat.png 0 → 100755

5.92 KB

app/assets/images/emoji/smoking.png 0 → 100755

2.81 KB

app/assets/images/emoji/snail.png 0 → 100755

6.5 KB

app/assets/images/emoji/snake.png 0 → 100755

3.97 KB

app/assets/images/emoji/snowboarder.png 0 → 100755

5.23 KB

app/assets/images/emoji/snowflake.png 0 → 100755

5.5 KB

app/assets/images/emoji/snowman.png 0 → 100755

4.55 KB

app/assets/images/emoji/sob.png 0 → 100755

5.58 KB

app/assets/images/emoji/soccer.png 0 → 100755

4.76 KB

app/assets/images/emoji/soon.png 0 → 100755

1.87 KB

app/assets/images/emoji/sos.png 0 → 100755

4.16 KB

app/assets/images/emoji/sound.png 0 → 100755

4.91 KB

app/assets/images/emoji/space_invader.png 0 → 100755

4.25 KB

app/assets/images/emoji/spades.png 0 → 100755

1.68 KB

app/assets/images/emoji/spaghetti.png 0 → 100755

6.79 KB

app/assets/images/emoji/sparkler.png 0 → 100755

5.56 KB

app/assets/images/emoji/sparkles.png 0 → 100755

2.16 KB

app/assets/images/emoji/speak_no_evil.png 0 → 100755

5.84 KB

app/assets/images/emoji/speaker.png 0 → 100755

5.05 KB

app/assets/images/emoji/speech_balloon.png 0 → 100755

2.08 KB

app/assets/images/emoji/speedboat.png 0 → 100755

3.43 KB

app/assets/images/emoji/squirrel.png 0 → 100755

9.13 KB

app/assets/images/emoji/star.png 0 → 100755

3.54 KB

app/assets/images/emoji/star2.png 0 → 100755

3.97 KB

app/assets/images/emoji/stars.png 0 → 100755

4.26 KB

app/assets/images/emoji/station.png 0 → 100755

4.72 KB

app/assets/images/emoji/statue_of_liberty.png 0 → 100755

5.93 KB

app/assets/images/emoji/steam_locomotive.png 0 → 100755

5.04 KB

app/assets/images/emoji/stew.png 0 → 100755

5.24 KB

app/assets/images/emoji/straight_ruler.png 0 → 100755

3.74 KB

app/assets/images/emoji/strawberry.png 0 → 100755

5.35 KB

app/assets/images/emoji/sun_with_face.png 0 → 100755

7.77 KB

app/assets/images/emoji/sunflower.png 0 → 100755

6.41 KB

app/assets/images/emoji/sunglasses.png 0 → 100755

5.61 KB

app/assets/images/emoji/sunny.png 0 → 100755

3.71 KB

app/assets/images/emoji/sunrise.png 0 → 100755

3.82 KB

app/assets/images/emoji/sunrise_over_mountains.png 0 → 100755

6.44 KB

app/assets/images/emoji/surfer.png 0 → 100755

6.11 KB

app/assets/images/emoji/sushi.png 0 → 100755

5.13 KB

app/assets/images/emoji/suspect.png 0 → 100755

1016 Bytes

app/assets/images/emoji/suspension_railway.png 0 → 100755

3.84 KB

app/assets/images/emoji/sweat.png 0 → 100755

5.45 KB

app/assets/images/emoji/sweat_drops.png 0 → 100755

4.67 KB

app/assets/images/emoji/sweat_smile.png 0 → 100755

6.37 KB

app/assets/images/emoji/sweet_potato.png 0 → 100755

5.55 KB

app/assets/images/emoji/swimmer.png 0 → 100755

4.28 KB

app/assets/images/emoji/symbols.png 0 → 100755

5.31 KB

app/assets/images/emoji/syringe.png 0 → 100755

2.97 KB

app/assets/images/emoji/tada.png 0 → 100755

5.81 KB

app/assets/images/emoji/tanabata_tree.png 0 → 100755

4.31 KB

app/assets/images/emoji/tangerine.png 0 → 100755

6.49 KB

app/assets/images/emoji/taurus.png 0 → 100755

4.62 KB

app/assets/images/emoji/taxi.png 0 → 100755

3.66 KB

app/assets/images/emoji/tea.png 0 → 100755

5.81 KB

app/assets/images/emoji/telephone.png 0 → 100755

5.37 KB

app/assets/images/emoji/telephone_receiver.png 0 → 100755

1.95 KB

app/assets/images/emoji/telescope.png 0 → 100755

3.24 KB

app/assets/images/emoji/tennis.png 0 → 100755

5.84 KB

app/assets/images/emoji/tent.png 0 → 100755

4.38 KB

app/assets/images/emoji/thought_balloon.png 0 → 100755

2.52 KB

app/assets/images/emoji/three.png 0 → 100755

3.67 KB

app/assets/images/emoji/thumbsdown.png 0 → 100755

4.95 KB

app/assets/images/emoji/thumbsup.png 0 → 100755

4.96 KB

app/assets/images/emoji/ticket.png 0 → 100755

3.02 KB

app/assets/images/emoji/tiger.png 0 → 100755

5.91 KB

app/assets/images/emoji/tiger2.png 0 → 100755

5.61 KB

app/assets/images/emoji/tired_face.png 0 → 100755

6.03 KB

app/assets/images/emoji/tm.png 0 → 100755

842 Bytes

app/assets/images/emoji/toilet.png 0 → 100755

1.69 KB

app/assets/images/emoji/tokyo_tower.png 0 → 100755

4.69 KB

app/assets/images/emoji/tomato.png 0 → 100755

5.61 KB

app/assets/images/emoji/tongue.png 0 → 100755

5.65 KB

app/assets/images/emoji/tongue2.png 0 → 100755

3.58 KB

app/assets/images/emoji/top.png 0 → 100755

3.7 KB

app/assets/images/emoji/tophat.png 0 → 100755

2.94 KB

app/assets/images/emoji/tractor.png 0 → 100755

5.54 KB

app/assets/images/emoji/traffic_light.png 0 → 100755

3.46 KB

app/assets/images/emoji/train.png 0 → 100755

3.81 KB

app/assets/images/emoji/train2.png 0 → 100755

4.7 KB

app/assets/images/emoji/tram.png 0 → 100755

4.75 KB

app/assets/images/emoji/triangular_flag_on_post.png 0 → 100755

1.37 KB

app/assets/images/emoji/triangular_ruler.png 0 → 100755

2.64 KB

app/assets/images/emoji/trident.png 0 → 100755

4.72 KB

app/assets/images/emoji/triumph.png 0 → 100755

6.02 KB

app/assets/images/emoji/trolleybus.png 0 → 100755

4.33 KB

app/assets/images/emoji/trollface.png 0 → 100755

4.79 KB

app/assets/images/emoji/trophy.png 0 → 100755

5.39 KB

app/assets/images/emoji/tropical_drink.png 0 → 100755

4.09 KB

app/assets/images/emoji/tropical_fish.png 0 → 100755

5.71 KB

app/assets/images/emoji/truck.png 0 → 100755

3.63 KB

app/assets/images/emoji/trumpet.png 0 → 100755

4.27 KB

app/assets/images/emoji/tshirt.png 0 → 100755

4.57 KB

app/assets/images/emoji/tulip.png 0 → 100755

5.92 KB

app/assets/images/emoji/turtle.png 0 → 100755

5.21 KB

app/assets/images/emoji/tv.png 0 → 100755

5.12 KB

app/assets/images/emoji/twisted_rightwards_arrows.png 0 → 100755

4.21 KB

app/assets/images/emoji/two.png 0 → 100755

3.44 KB

app/assets/images/emoji/two_hearts.png 0 → 100755

3.48 KB

app/assets/images/emoji/two_men_holding_hands.png 0 → 100755

6.83 KB

app/assets/images/emoji/two_women_holding_hands.png 0 → 100755

7.45 KB

app/assets/images/emoji/u5272.png 0 → 100755

4.43 KB

app/assets/images/emoji/u5408.png 0 → 100755

3.8 KB

app/assets/images/emoji/u55b6.png 0 → 100755

3.33 KB

app/assets/images/emoji/u6307.png 0 → 100755

4.01 KB

app/assets/images/emoji/u6708.png 0 → 100755

2.94 KB

app/assets/images/emoji/u6709.png 0 → 100755

3.12 KB

app/assets/images/emoji/u6e80.png 0 → 100755

4.32 KB

app/assets/images/emoji/u7121.png 0 → 100755

3.85 KB

app/assets/images/emoji/u7533.png 0 → 100755

2.98 KB

app/assets/images/emoji/u7981.png 0 → 100755

5.05 KB

app/assets/images/emoji/u7a7a.png 0 → 100755

4.08 KB

app/assets/images/emoji/uk.png 0 → 100755

5.76 KB

app/assets/images/emoji/umbrella.png 0 → 100755

4.63 KB

app/assets/images/emoji/unamused.png 0 → 100755

5.19 KB

app/assets/images/emoji/underage.png 0 → 100755

5.59 KB

app/assets/images/emoji/unlock.png 0 → 100755

3.47 KB

app/assets/images/emoji/up.png 0 → 100755

3.63 KB

app/assets/images/emoji/us.png 0 → 100755

6.14 KB

app/assets/images/emoji/v.png 0 → 100755

4.56 KB

app/assets/images/emoji/vertical_traffic_light.png 0 → 100755

3.34 KB

app/assets/images/emoji/vhs.png 0 → 100755

3.07 KB

app/assets/images/emoji/vibration_mode.png 0 → 100755

3.81 KB

app/assets/images/emoji/video_camera.png 0 → 100755

4.97 KB

app/assets/images/emoji/video_game.png 0 → 100755

4.84 KB

app/assets/images/emoji/violin.png 0 → 100755

4.9 KB

app/assets/images/emoji/virgo.png 0 → 100755

4.75 KB

app/assets/images/emoji/volcano.png 0 → 100755

6.02 KB

app/assets/images/emoji/vs.png 0 → 100755

3.34 KB

app/assets/images/emoji/walking.png 0 → 100755

2.41 KB

app/assets/images/emoji/waning_crescent_moon.png 0 → 100755

5.75 KB

app/assets/images/emoji/waning_gibbous_moon.png 0 → 100755

6.36 KB

app/assets/images/emoji/warning.png 0 → 100755

3.1 KB

app/assets/images/emoji/watch.png 0 → 100755

5.07 KB

app/assets/images/emoji/water_buffalo.png 0 → 100755

4.66 KB

app/assets/images/emoji/watermelon.png 0 → 100755

5.37 KB

app/assets/images/emoji/wave.png 0 → 100755

4.93 KB

app/assets/images/emoji/wavy_dash.png 0 → 100755

872 Bytes

app/assets/images/emoji/waxing_crescent_moon.png 0 → 100755

6.05 KB

app/assets/images/emoji/waxing_gibbous_moon.png 0 → 100755

6.3 KB

app/assets/images/emoji/wc.png 0 → 100755

3.99 KB

app/assets/images/emoji/weary.png 0 → 100755

6.13 KB

app/assets/images/emoji/wedding.png 0 → 100755

5.71 KB

app/assets/images/emoji/whale.png 0 → 100755

4.82 KB

app/assets/images/emoji/whale2.png 0 → 100755

5.89 KB

app/assets/images/emoji/wheelchair.png 0 → 100755

4.13 KB

app/assets/images/emoji/white_circle.png 0 → 100755

2.45 KB

app/assets/images/emoji/white_flower.png 0 → 100755

4.29 KB

app/assets/images/emoji/white_square.png 0 → 100755

1.38 KB

app/assets/images/emoji/wind_chime.png 0 → 100755

3.41 KB

app/assets/images/emoji/wine_glass.png 0 → 100755

3.08 KB

app/assets/images/emoji/wink.png 0 → 100755

5.13 KB

app/assets/images/emoji/wink2.png 0 → 100755

5.87 KB

app/assets/images/emoji/wolf.png 0 → 100755

4.73 KB

app/assets/images/emoji/woman.png 0 → 100755

6.73 KB

app/assets/images/emoji/womans_clothes.png 0 → 100755

3.98 KB

app/assets/images/emoji/womans_hat.png 0 → 100755

7.91 KB

app/assets/images/emoji/womens.png 0 → 100755

3.8 KB

app/assets/images/emoji/wrench.png 0 → 100755

2.71 KB

app/assets/images/emoji/x.png 0 → 100755

2 KB

app/assets/images/emoji/yellow_heart.png 0 → 100755

4.31 KB

app/assets/images/emoji/yen.png 0 → 100755

4.87 KB

app/assets/images/emoji/yum.png 0 → 100755

5.75 KB

app/assets/images/emoji/zap.png 0 → 100755

2.18 KB

app/assets/images/emoji/zero.png 0 → 100755

3.51 KB

app/assets/images/emoji/zzz.png 0 → 100755

1.98 KB

app/assets/javascripts/admin.js
... ... @@ -1,11 +0,0 @@
1   -$(document).ready(function(){
2   - $('input#user_force_random_password').on('change', function(elem) {
3   - var elems = $('#user_password, #user_password_confirmation');
4   -
5   - if ($(this).attr('checked')) {
6   - elems.val('').attr('disabled', true);
7   - } else {
8   - elems.removeAttr('disabled');
9   - }
10   - });
11   -});
app/assets/javascripts/admin.js.coffee 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +$ ->
  2 + $('input#user_force_random_password').on 'change', (elem) ->
  3 + elems = $('#user_password, #user_password_confirmation')
  4 +
  5 + if $(@).attr 'checked'
  6 + elems.val('').attr 'disabled', true
  7 + else
  8 + elems.removeAttr 'disabled'
... ...
app/assets/javascripts/application.js
... ... @@ -17,134 +17,3 @@
17 17 //= require raphael
18 18 //= require branch-graph
19 19 //= require_tree .
20   -
21   -$(document).ready(function(){
22   -
23   - $(".one_click_select").live("click", function(){
24   - $(this).select();
25   - });
26   -
27   - $('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
28   - var buttons = $('[type="submit"]', this);
29   - switch( e.type ){
30   - case 'ajax:beforeSend':
31   - case 'submit':
32   - buttons.attr('disabled', 'disabled');
33   - break;
34   - case ' ajax:complete':
35   - default:
36   - buttons.removeAttr('disabled');
37   - break;
38   - }
39   - })
40   -
41   - $(".account-box").mouseenter(showMenu);
42   - $(".account-box").mouseleave(resetMenu);
43   -
44   - $("#projects-list .project").live('click', function(e){
45   - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
46   - location.href = $(this).attr("url");
47   - e.stopPropagation();
48   - return false;
49   - }
50   - });
51   -
52   - /**
53   - * Focus search field by pressing 's' key
54   - */
55   - $(document).keypress(function(e) {
56   - if( $(e.target).is(":input") ) return;
57   - switch(e.which) {
58   - case 115: focusSearch();
59   - e.preventDefault();
60   - }
61   - });
62   -
63   - /**
64   - * Commit show suppressed diff
65   - *
66   - */
67   - $(".supp_diff_link").bind("click", function() {
68   - showDiff(this);
69   - });
70   -
71   - /**
72   - * Note markdown preview
73   - *
74   - */
75   - $(document).on('click', '#preview-link', function(e) {
76   - $('#preview-note').text('Loading...');
77   -
78   - var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview');
79   - $(this).text(previewLinkText);
80   -
81   - var note = $('#note_note').val();
82   - if (note.trim().length === 0) { note = 'Nothing to preview'; }
83   - $.post($(this).attr('href'), {note: note}, function(data) {
84   - $('#preview-note').html(data);
85   - });
86   -
87   - $('#preview-note, #note_note').toggle();
88   - e.preventDefault();
89   - });
90   -});
91   -
92   -function focusSearch() {
93   - $("#search").focus();
94   -}
95   -
96   -function updatePage(data){
97   - $.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
98   -}
99   -
100   -function showMenu() {
101   - $(this).toggleClass('hover');
102   -}
103   -
104   -function resetMenu() {
105   - $(this).removeClass("hover");
106   -}
107   -
108   -function slugify(text) {
109   - return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
110   -}
111   -
112   -function showDiff(link) {
113   - $(link).next('table').show();
114   - $(link).remove();
115   -}
116   -
117   -(function($){
118   - var _chosen = $.fn.chosen;
119   - $.fn.extend({
120   - chosen: function(options) {
121   - var default_options = {'search_contains' : 'true'};
122   - $.extend(default_options, options);
123   - return _chosen.apply(this, [default_options]);
124   - }})
125   -})(jQuery);
126   -
127   -
128   -function ajaxGet(url) {
129   - $.ajax({type: "GET", url: url, dataType: "script"});
130   -}
131   -
132   -/**
133   - * Disable button if text field is empty
134   - */
135   -function disableButtonIfEmtpyField(field_selector, button_selector) {
136   - field = $(field_selector);
137   - if(field.val() == "") {
138   - field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled");
139   - }
140   -
141   - field.on('keyup', function(){
142   - var field = $(this);
143   - var closest_submit = field.closest("form").find(button_selector);
144   - if(field.val() == "") {
145   - closest_submit.attr("disabled", "disabled").addClass("disabled");
146   - } else {
147   - closest_submit.removeAttr("disabled").removeClass("disabled");
148   - }
149   - })
150   -}
... ...
app/assets/javascripts/graph.js
... ... @@ -1,10 +0,0 @@
1   -function initGraphNav() {
2   - $(".graph svg").css("position", "relative");
3   - $("body").bind("keyup", function(e) {
4   - if(e.keyCode == 37) { // left
5   - $(".graph svg").animate({ left: "+=400" });
6   - } else if(e.keyCode == 39) { // right
7   - $(".graph svg").animate({ left: "-=400" });
8   - }
9   - });
10   -}
app/assets/javascripts/graph.js.coffee 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +initGraphNav = ->
  2 + $('.graph svg').css 'position', 'relative'
  3 +
  4 + $('body').bind 'keyup', (e) ->
  5 + if e.keyCode is 37 # left
  6 + $('.graph svg').animate left: '+=400'
  7 + else if e.keyCode is 39 # right
  8 + $('.graph svg').animate left: '-=400'
  9 +
  10 +window.initGraphNav = initGraphNav
... ...
app/assets/javascripts/issues.js
... ... @@ -80,6 +80,10 @@ function issuesPage(){
80 80 $(this).closest("form").submit();
81 81 });
82 82  
  83 + $("#new_issue_link").click(function(){
  84 + updateNewIssueURL();
  85 + });
  86 +
83 87 $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
84 88 var t = $(this),
85 89 totalIssues,
... ... @@ -126,3 +130,20 @@ function issuesCheckChanged() {
126 130 $('.issues_filters').show();
127 131 }
128 132 }
  133 +
  134 +function updateNewIssueURL(){
  135 + var new_issue_link = $("#new_issue_link");
  136 + var milestone_id = $("#milestone_id").val();
  137 + var assignee_id = $("#assignee_id").val();
  138 + var new_href = "";
  139 + if(milestone_id){
  140 + new_href = "issue[milestone_id]=" + milestone_id + "&";
  141 + }
  142 + if(assignee_id){
  143 + new_href = new_href + "issue[assignee_id]=" + assignee_id;
  144 + }
  145 + if(new_href.length){
  146 + new_href = new_issue_link.attr("href") + "?" + new_href;
  147 + new_issue_link.attr("href", new_href);
  148 + }
  149 +};
... ...
app/assets/javascripts/loader.js
... ... @@ -1,11 +0,0 @@
1   -var Loader = {
2   - img_src: "/assets/ajax-loader.gif",
3   -
4   - html:
5   - function(width) {
6   - img = $("<img>");
7   - img.attr("width", width);
8   - img.attr("src", this.img_src);
9   - return img;
10   - }
11   -}
app/assets/javascripts/loader.js.coffee 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +Loader =
  2 + html: (width) ->
  3 + $('<img>').attr src: '/assets/ajax-loader.gif', width: width
  4 +
  5 +window.Loader = Loader
... ...
app/assets/javascripts/main.js 0 → 100644
... ... @@ -0,0 +1,130 @@
  1 +$(document).ready(function(){
  2 +
  3 + $(".one_click_select").live("click", function(){
  4 + $(this).select();
  5 + });
  6 +
  7 + $('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
  8 + var buttons = $('[type="submit"]', this);
  9 + switch( e.type ){
  10 + case 'ajax:beforeSend':
  11 + case 'submit':
  12 + buttons.attr('disabled', 'disabled');
  13 + break;
  14 + case ' ajax:complete':
  15 + default:
  16 + buttons.removeAttr('disabled');
  17 + break;
  18 + }
  19 + })
  20 +
  21 + $(".account-box").mouseenter(showMenu);
  22 + $(".account-box").mouseleave(resetMenu);
  23 +
  24 + $("#projects-list .project").live('click', function(e){
  25 + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
  26 + location.href = $(this).attr("url");
  27 + e.stopPropagation();
  28 + return false;
  29 + }
  30 + });
  31 +
  32 + /**
  33 + * Focus search field by pressing 's' key
  34 + */
  35 + $(document).keypress(function(e) {
  36 + if( $(e.target).is(":input") ) return;
  37 + switch(e.which) {
  38 + case 115: focusSearch();
  39 + e.preventDefault();
  40 + }
  41 + });
  42 +
  43 + /**
  44 + * Commit show suppressed diff
  45 + *
  46 + */
  47 + $(".supp_diff_link").bind("click", function() {
  48 + showDiff(this);
  49 + });
  50 +
  51 + /**
  52 + * Note markdown preview
  53 + *
  54 + */
  55 + $(document).on('click', '#preview-link', function(e) {
  56 + $('#preview-note').text('Loading...');
  57 +
  58 + var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview');
  59 + $(this).text(previewLinkText);
  60 +
  61 + var note = $('#note_note').val();
  62 + if (note.trim().length === 0) { note = 'Nothing to preview'; }
  63 + $.post($(this).attr('href'), {note: note}, function(data) {
  64 + $('#preview-note').html(data);
  65 + });
  66 +
  67 + $('#preview-note, #note_note').toggle();
  68 + e.preventDefault();
  69 + });
  70 +});
  71 +
  72 +function focusSearch() {
  73 + $("#search").focus();
  74 +}
  75 +
  76 +function updatePage(data){
  77 + $.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
  78 +}
  79 +
  80 +function showMenu() {
  81 + $(this).toggleClass('hover');
  82 +}
  83 +
  84 +function resetMenu() {
  85 + $(this).removeClass("hover");
  86 +}
  87 +
  88 +function slugify(text) {
  89 + return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
  90 +}
  91 +
  92 +function showDiff(link) {
  93 + $(link).next('table').show();
  94 + $(link).remove();
  95 +}
  96 +
  97 +(function($){
  98 + var _chosen = $.fn.chosen;
  99 + $.fn.extend({
  100 + chosen: function(options) {
  101 + var default_options = {'search_contains' : 'true'};
  102 + $.extend(default_options, options);
  103 + return _chosen.apply(this, [default_options]);
  104 + }})
  105 +})(jQuery);
  106 +
  107 +
  108 +function ajaxGet(url) {
  109 + $.ajax({type: "GET", url: url, dataType: "script"});
  110 +}
  111 +
  112 +/**
  113 + * Disable button if text field is empty
  114 + */
  115 +function disableButtonIfEmtpyField(field_selector, button_selector) {
  116 + field = $(field_selector);
  117 + if(field.val() == "") {
  118 + field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled");
  119 + }
  120 +
  121 + field.on('keyup', function(){
  122 + var field = $(this);
  123 + var closest_submit = field.closest("form").find(button_selector);
  124 + if(field.val() == "") {
  125 + closest_submit.attr("disabled", "disabled").addClass("disabled");
  126 + } else {
  127 + closest_submit.removeAttr("disabled").removeClass("disabled");
  128 + }
  129 + })
  130 +}
... ...
app/assets/javascripts/projects.js.coffee
1 1 window.Projects = ->
2   - $("#project_name").live "change", ->
3   - slug = slugify($(this).val())
4   - $("#project_code").val(slug)
5   - $("#project_path").val(slug)
  2 + $('#project_name').on 'change', ->
  3 + slug = slugify $(@).val()
  4 + $('#project_code, #project_path').val slug
6 5  
7   - $(".new_project, .edit_project").live "ajax:before", ->
8   - $(".project_new_holder, .project_edit_holder").hide()
9   - $(".save-project-loader").show()
  6 + $('.new_project, .edit_project').on 'ajax:before', ->
  7 + $('.project_new_holder, .project_edit_holder').hide()
  8 + $('.save-project-loader').show()
10 9  
11   - $("form #project_default_branch").chosen()
12   - disableButtonIfEmtpyField "#project_name", ".project-submit"
  10 + $('form #project_default_branch').chosen()
  11 + disableButtonIfEmtpyField '#project_name', '.project-submit'
13 12  
14 13 # Git clone panel switcher
15 14 $ ->
16   - scope = $('.project_clone_holder')
  15 + scope = $ '.project_clone_holder'
17 16 if scope.length > 0
18 17 $('a, button', scope).click ->
19   - $('a, button', scope).removeClass('active')
20   - $(this).addClass('active')
21   - $('#project_clone', scope).val($(this).data('clone'))
  18 + $('a, button', scope).removeClass 'active'
  19 + $(@).addClass 'active'
  20 + $('#project_clone', scope).val $(@).data 'clone'
... ...
app/assets/javascripts/snippets.js
... ... @@ -1,9 +0,0 @@
1   -$(document).ready(function(){
2   - $("#snippets-table .snippet").live('click', function(e){
3   - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
4   - location.href = $(this).attr("url");
5   - e.stopPropagation();
6   - return false;
7   - }
8   - });
9   -});
app/assets/javascripts/snippets.js.coffee 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +$ ->
  2 + $('#snippets-table .snippet').live 'click', (e) ->
  3 + if e.target.nodeName isnt 'A' and e.target.nodeName isnt 'INPUT'
  4 + location.href = $(@).attr 'url'
  5 + e.stopPropagation()
  6 + false
... ...
app/assets/javascripts/team.js
... ... @@ -1,8 +0,0 @@
1   -function backToMembers(){
2   - $("#new_team_member").hide("slide", { direction: "right" }, 150, function(){
3   - $("#team-table").show("slide", { direction: "left" }, 150, function() {
4   - $("#new_team_member").remove();
5   - $(".add_new").show();
6   - });
7   - });
8   -}
app/assets/stylesheets/main.scss
1 1 @import "bootstrap";
2 2 @import "bootstrap-responsive";
3 3  
4   -/** GITLAB colors **/
  4 +/** GitLab colors **/
5 5 $link_color:#3A89A3;
6 6 $blue_link: #2fa0bb;
7 7 $style_color: #474d57;
8 8 $hover: #fdf5d9;
9 9  
10   -/** GITLAB Fonts **/
  10 +/** GitLab Fonts **/
11 11 @font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); }
12 12  
13 13 /** MIXINS **/
... ... @@ -113,9 +113,9 @@ $hover: #fdf5d9;
113 113 @import "themes/ui_modern.scss";
114 114  
115 115 /**
116   - * Gitlab bootstrap.
  116 + * GitLab bootstrap.
117 117 * Overrides some styles of twitter bootstrap.
118   - * Also give some common classes for gitlab app
  118 + * Also give some common classes for GitLab app
119 119 */
120 120 @import "gitlab_bootstrap/common.scss";
121 121 @import "gitlab_bootstrap/typography.scss";
... ...
app/controllers/application_controller.rb
... ... @@ -11,15 +11,11 @@ class ApplicationController &lt; ActionController::Base
11 11 helper_method :abilities, :can?
12 12  
13 13 rescue_from Gitlab::Gitolite::AccessDenied do |exception|
14   - render "errors/gitolite", layout: "error"
15   - end
16   -
17   - rescue_from Gitlab::Gitolite::InvalidKey do |exception|
18   - render "errors/invalid_ssh_key", layout: "error"
  14 + render "errors/gitolite", layout: "error", status: 500
19 15 end
20 16  
21 17 rescue_from Encoding::CompatibilityError do |exception|
22   - render "errors/encoding", layout: "error", status: 404
  18 + render "errors/encoding", layout: "error", status: 500
23 19 end
24 20  
25 21 rescue_from ActiveRecord::RecordNotFound do |exception|
... ...
app/controllers/commits_controller.rb
... ... @@ -64,19 +64,14 @@ class CommitsController &lt; ApplicationController
64 64 @commit.to_patch,
65 65 type: "text/plain",
66 66 disposition: 'attachment',
67   - filename: (@commit.id.to_s + ".patch")
  67 + filename: "#{@commit.id.patch}"
68 68 )
69 69 end
70 70  
71 71 protected
72 72  
73 73 def load_refs
74   - if params[:ref].blank?
75   - @branch = params[:branch].blank? ? nil : params[:branch]
76   - @tag = params[:tag].blank? ? nil : params[:tag]
77   - @ref = @branch || @tag || @project.try(:default_branch) || 'master'
78   - else
79   - @ref = params[:ref]
80   - end
  74 + @ref ||= params[:ref].presence || params[:branch].presence || params[:tag].presence
  75 + @ref ||= @ref || @project.try(:default_branch) || 'master'
81 76 end
82 77 end
... ...
app/controllers/issues_controller.rb
... ... @@ -37,7 +37,7 @@ class IssuesController &lt; ApplicationController
37 37 end
38 38  
39 39 def new
40   - @issue = @project.issues.new
  40 + @issue = @project.issues.new(params[:issue])
41 41 respond_with(@issue)
42 42 end
43 43  
... ...
app/controllers/team_members_controller.rb
... ... @@ -17,13 +17,12 @@ class TeamMembersController &lt; ApplicationController
17 17 end
18 18  
19 19 def create
20   - @team_member = UsersProject.new(params[:team_member])
21   - @team_member.project = project
22   - if @team_member.save
23   - redirect_to team_project_path(@project)
24   - else
25   - render "new"
26   - end
  20 + @project.add_users_ids_to_team(
  21 + params[:user_ids],
  22 + params[:project_access]
  23 + )
  24 +
  25 + redirect_to team_project_path(@project)
27 26 end
28 27  
29 28 def update
... ...
app/helpers/gitlab_markdown_helper.rb
1 1 module GitlabMarkdownHelper
2   - # Replaces references (i.e. @abc, #123, !456, ...) in the text with links to
3   - # the appropriate items in Gitlab.
4   - #
5   - # text - the source text
6   - # html_options - extra options for the reference links as given to link_to
7   - #
8   - # note: reference links will only be generated if @project is set
9   - #
10   - # see Gitlab::Markdown for details on the supported syntax
11   - def gfm(text, html_options = {})
12   - return text if text.nil?
13   - return text if @project.nil?
14   -
15   - # Extract pre blocks so they are not altered
16   - # from http://github.github.com/github-flavored-markdown/
17   - extractions = {}
18   - text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
19   - md5 = Digest::MD5.hexdigest(match)
20   - extractions[md5] = match
21   - "{gfm-extraction-#{md5}}"
22   - end
23   -
24   - # TODO: add popups with additional information
25   -
26   - parser = Gitlab::Markdown.new(@project, html_options)
27   - text = parser.parse(text)
28   -
29   - # Insert pre block extractions
30   - text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
31   - extractions[$1]
32   - end
33   -
34   - sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class )
35   - end
  2 + include Gitlab::Markdown
36 3  
37 4 # Use this in places where you would normally use link_to(gfm(...), ...).
38 5 #
... ... @@ -60,7 +27,7 @@ module GitlabMarkdownHelper
60 27 filter_html: true,
61 28 with_toc_data: true,
62 29 hard_wrap: true)
63   - @markdown ||= Redcarpet::Markdown.new(gitlab_renderer,
  30 + @markdown = Redcarpet::Markdown.new(gitlab_renderer,
64 31 # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
65 32 no_intra_emphasis: true,
66 33 tables: true,
... ...
app/helpers/projects_helper.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +module ProjectsHelper
  2 + def grouper_project_members(project)
  3 + @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
  4 + end
  5 +end
  6 +
... ...
app/mailers/notify.rb
... ... @@ -111,18 +111,18 @@ class Notify &lt; ActionMailer::Base
111 111 # Examples
112 112 #
113 113 # >> subject('Lorem ipsum')
114   - # => "gitlab | Lorem ipsum"
  114 + # => "GitLab | Lorem ipsum"
115 115 #
116 116 # # Automatically inserts Project name when @project is set
117 117 # >> @project = Project.last
118 118 # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
119 119 # >> subject('Lorem ipsum')
120   - # => "gitlab | Lorem ipsum | Ruby on Rails"
  120 + # => "GitLab | Lorem ipsum | Ruby on Rails"
121 121 #
122 122 # # Accepts multiple arguments
123 123 # >> subject('Lorem ipsum', 'Dolor sit amet')
124   - # => "gitlab | Lorem ipsum | Dolor sit amet"
  124 + # => "GitLab | Lorem ipsum | Dolor sit amet"
125 125 def subject(*extra)
126   - "gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
  126 + "GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
127 127 end
128 128 end
... ...
app/models/note.rb
... ... @@ -103,7 +103,7 @@ class Note &lt; ActiveRecord::Base
103 103 # Returns true if this is an upvote note,
104 104 # otherwise false is returned
105 105 def upvote?
106   - note =~ /^\+1/ ? true : false
  106 + note.start_with?('+1') || note.start_with?(':+1:')
107 107 end
108 108 end
109 109 # == Schema Information
... ...
app/models/users_project.rb
... ... @@ -14,7 +14,7 @@ class UsersProject &lt; ActiveRecord::Base
14 14 after_save :update_repository
15 15 after_destroy :update_repository
16 16  
17   - validates_uniqueness_of :user_id, scope: [:project_id]
  17 + validates_uniqueness_of :user_id, scope: [:project_id], message: "already exists in project"
18 18 validates_presence_of :user_id
19 19 validates_presence_of :project_id
20 20  
... ... @@ -65,10 +65,10 @@ class UsersProject &lt; ActiveRecord::Base
65 65  
66 66 def self.access_roles
67 67 {
68   - "Guest" => GUEST,
69   - "Reporter" => REPORTER,
  68 + "Guest" => GUEST,
  69 + "Reporter" => REPORTER,
70 70 "Developer" => DEVELOPER,
71   - "Master" => MASTER
  71 + "Master" => MASTER
72 72 }
73 73 end
74 74  
... ...
app/views/errors/encoding.html.haml
1   -.alert-message.block-message.error
2   - %h3 Encoding Error
3   - %hr
4   - %p
5   - Page can't be loaded because of an encoding error.
  1 +%h1 Encoding Error
  2 +%hr
  3 +%p Page can't be loaded because of an encoding error.
... ...
app/views/errors/gitolite.html.haml
1 1 %h1 Git Error
2 2 %hr
3   -%h2 Gitlab was unable to access your Gitolite system.
  3 +%h2 GitLab was unable to access your Gitolite system.
4 4  
5 5 .git_error_tips
6 6 %h4 Tips for Administrator:
... ...
app/views/errors/invalid_ssh_key.html.haml
... ... @@ -1,3 +0,0 @@
1   -%h1 Git Error
2   -%hr
3   -%p Seems like SSH Key you provided is not a valid SSH key.
app/views/help/index.html.haml
... ... @@ -30,7 +30,7 @@
30 30 %h5= link_to "API", help_api_path
31 31  
32 32 %li
33   - %h5= link_to "Gitlab Markdown", help_markdown_path
  33 + %h5= link_to "GitLab Markdown", help_markdown_path
34 34  
35 35 %li
36 36 %h5= link_to "SSH keys", help_ssh_path
... ...
app/views/help/markdown.html.haml
1   -%h3.page_title Gitlab Flavored Markdown
  1 +%h3.page_title GitLab Flavored Markdown
2 2 .back_link
3 3 = link_to help_path do
4 4 &larr; to index
... ... @@ -7,7 +7,7 @@
7 7 .row
8 8 .span8
9 9 %p
10   - For Gitlab we developed something we call "Gitlab Flavored Markdown" (GFM).
  10 + For GitLab we developed something we call "GitLab Flavored Markdown" (GFM).
11 11 It extends the standard Markdown in a few significant ways adds some useful functionality.
12 12  
13 13 %p You can use GFM in:
... ... @@ -20,6 +20,15 @@
20 20 %li milestones
21 21 %li wiki pages
22 22  
  23 + .span4
  24 + .alert.alert-info
  25 + %p
  26 + If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent
  27 + %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax"
  28 + at Daring Fireball.
  29 +
  30 +.row
  31 + .span8
23 32 %h3 Differences from traditional Markdown
24 33  
25 34 %h4 Newlines
... ... @@ -62,7 +71,30 @@
62 71 %p becomes
63 72 = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```}
64 73  
65   - %h4 Special Gitlab references
  74 + %h4 Emoji
  75 +
  76 +.row
  77 + .span8
  78 + :ruby
  79 + puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you:
  80 +
  81 + :exclamation: You can use emoji anywhere GFM is supported. :sunglasses:
  82 +
  83 + You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that.
  84 +
  85 + If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
  86 + }
  87 +
  88 + .span4
  89 + .alert.alert-info
  90 + %p
  91 + Consult the
  92 + %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/"
  93 + for a list of all supported emoji codes.
  94 +
  95 +.row
  96 + .span8
  97 + %h4 Special GitLab references
66 98  
67 99 %p
68 100 GFM recognizes special references.
... ... @@ -88,18 +120,10 @@
88 120 for commits
89 121  
90 122 -# this example will only be shown if the user has a project with at least one issue
91   - - if project = current_user.projects.first
92   - - if issue = project.issues.first
93   - %p For example in your #{link_to project.name, project_path(project)} project something like
  123 + - if @project = current_user.projects.first
  124 + - if issue = @project.issues.first
  125 + %p For example in your #{link_to @project.name, project_path(@project)} project, writing:
94 126 %pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it."
95   - %p becomes
  127 + %p becomes:
96 128 = markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it."
97   -
98   -
99   -
100   - .span4.right
101   - .alert.alert-info
102   - %p
103   - If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent
104   - %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax"
105   - at Daring Fireball.
  129 + - @project = nil # Prevent this from bubbling up to page title
... ...
app/views/help/ssh.html.haml
... ... @@ -5,7 +5,7 @@
5 5 %hr
6 6  
7 7 %p.slead
8   - SSH key allows you to establish a secure connection between your computer and Gitlab
  8 + SSH key allows you to establish a secure connection between your computer and GitLab
9 9  
10 10 %p.slead
11 11 To generate a new SSH key just open your terminal and use code below.
... ... @@ -17,7 +17,7 @@
17 17 \# Generating public/private rsa key pair...
18 18  
19 19 %p.slead
20   - Next just use code below to dump your public key and add to GITLAB SSH Keys
  20 + Next just use code below to dump your public key and add to GitLab SSH Keys
21 21  
22 22 %pre.dark
23 23 cat ~/.ssh/id_rsa.pub
... ...
app/views/help/system_hooks.html.haml
... ... @@ -5,7 +5,7 @@
5 5 %hr
6 6  
7 7 %p.slead
8   - Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
  8 + Your GitLab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
9 9 %br
10 10 System Hooks can be used for logging or change information in LDAP server.
11 11 %br
... ...
app/views/help/web_hooks.html.haml
... ... @@ -5,11 +5,11 @@
5 5 %hr
6 6  
7 7 %p.slead
8   - Every Gitlab project can trigger a web server whenever the repo is pushed to.
  8 + Every GitLab project can trigger a web server whenever the repo is pushed to.
9 9 %br
10 10 Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
11 11 %br
12   - GITLAB will send POST request with commits information on every push.
  12 + GitLab will send POST request with commits information on every push.
13 13 %h5 Hooks request example:
14 14 = render "hooks/data_ex"
15 15  
... ...
app/views/help/workflow.html.haml
... ... @@ -24,7 +24,7 @@
24 24 git commit -am "My feature is ready"
25 25  
26 26 %li
27   - %p Push your branch to gitlabhq
  27 + %p Push your branch to GitLab
28 28 .bash
29 29 %pre.dark
30 30 git push origin $feature_name
... ...
app/views/hooks/_data_ex.html.erb
... ... @@ -32,7 +32,7 @@
32 32 :timestamp => "2012-01-03T23:36:29+02:00",
33 33 :url => "http://localhost/diaspora/commits/da1560886d...",
34 34 :author => {
35   - :name => "gitlab dev user",
  35 + :name => "GitLab dev user",
36 36 :email => "gitlabdev@dv6700.(none)"
37 37 }
38 38 }
... ...
app/views/issues/_form.html.haml
... ... @@ -38,7 +38,7 @@
38 38 = f.label :description, "Details"
39 39 .input
40 40 = f.text_area :description, maxlength: 2000, class: "xxlarge", rows: 14
41   - %p.hint Issues are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}.
  41 + %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
42 42  
43 43  
44 44 .actions
... ...
app/views/issues/index.html.haml
... ... @@ -6,7 +6,7 @@
6 6 .right
7 7 .span5
8 8 - if can? current_user, :write_issue, @project
9   - = link_to new_project_issue_path(@project), class: "right btn", title: "New Issue", remote: true do
  9 + = link_to new_project_issue_path(@project), class: "right btn", title: "New Issue", remote: true, id: "new_issue_link" do
10 10 %i.icon-plus
11 11 New Issue
12 12 = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
... ...
app/views/keys/index.html.haml
... ... @@ -3,8 +3,8 @@
3 3 = link_to "Add new", new_key_path, class: "btn right"
4 4  
5 5 %hr
6   -%p.slead
7   - SSH key allows you to establish a secure connection between your computer and Gitlab
  6 +%p.slead
  7 + SSH key allows you to establish a secure connection between your computer and GitLab
8 8  
9 9  
10 10 %table#keys-table
... ... @@ -15,7 +15,7 @@
15 15 %th
16 16 - @keys.each do |key|
17 17 = render(partial: 'show', locals: {key: key})
18   - - if @keys.blank?
  18 + - if @keys.blank?
19 19 %tr
20 20 %td{colspan: 3}
21 21 %h3.nothing_here_message There are no SSH keys with access to your account.
... ...
app/views/layouts/notify.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %head
3 3 %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
4 4 %title
5   - gitlabhq
  5 + GitLab
6 6 :css
7 7 .header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;}
8 8 .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;}
... ... @@ -21,7 +21,7 @@
21 21
22 22 %td{align: "left", style: "padding: 18px 0 10px;", width: "580"}
23 23 %h1{style: "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"}
24   - gitlab
  24 + GITLAB
25 25 - if @project
26 26 | #{@project.name}
27 27 %table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"}
... ...
app/views/milestones/_form.html.haml
... ... @@ -22,7 +22,7 @@
22 22 = f.label :description, "Description", class: "control-label"
23 23 .controls
24 24 = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10
25   - %p.hint Milestones are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}.
  25 + %p.hint Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
26 26 .span6
27 27 .control-group
28 28 = f.label :due_date, "Due Date", class: "control-label"
... ...
app/views/notes/_form.html.haml
... ... @@ -11,7 +11,7 @@
11 11 = f.text_area :note, size: 255, class: 'note-text'
12 12 #preview-note.preview_note.hide
13 13 .hint
14   - .right Comments are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}.
  14 + .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
15 15 .clearfix
16 16  
17 17 .row.note_advanced_opts.hide
... ...
app/views/notify/new_user_email.html.haml
... ... @@ -6,7 +6,7 @@
6 6 %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 7 Hi #{@user['name']}!
8 8 %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "}
9   - Administrator created account for you. Now you are a member of company gitlab application.
  9 + Administrator created account for you. Now you are a member of company GitLab application.
10 10 %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
11 11 %tr
12 12 %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
... ...
app/views/projects/_team.html.haml
1   -%table
2   - %thead
3   - %tr
4   - %th User
5   - %th Permissions
6   - %tbody
7   - - @project.users_projects.each do |up|
8   - = render(partial: 'team_members/show', locals: {member: up})
  1 +- grouper_project_members(@project).each do |access, members|
  2 + %table
  3 + %thead
  4 + %tr
  5 + %th.span7
  6 + = Project.access_options.key(access).pluralize
  7 + %th
  8 + %tbody
  9 + - members.each do |up|
  10 + = render(partial: 'team_members/show', locals: {member: up})
9 11  
10 12  
11 13 :javascript
... ...
app/views/team_members/_form.html.haml
1   -%h3= "New Team member"
  1 +%h3.page_title
  2 + = "New Team member(s)"
2 3 %hr
3 4 = form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f|
4 5 -if @team_member.errors.any?
... ... @@ -7,27 +8,23 @@
7 8 - @team_member.errors.full_messages.each do |msg|
8 9 %li= msg
9 10  
  11 + %h6 1. Choose people you want in the team
10 12 .clearfix
11   - = f.label :user_id, "Name"
12   - .input= f.select(:user_id, User.not_in_project(@project).all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, { style: "width:300px" })
  13 + = f.label :user_ids, "Peolpe"
  14 + .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), { class: "xxlarge", multiple: true })
13 15  
14 16  
  17 + %h6 2. Set access level for them
15 18 .clearfix
16 19 = f.label :project_access, "Project Access"
17   - .input= f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select"
  20 + .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select"
18 21  
19 22  
20 23 .actions
21   - = f.submit 'Save', class: "btn primary"
22   - = link_to "Cancel", team_project_path(@project), class: "btn"
  24 + = f.submit 'Save', class: "btn save-btn"
  25 + = link_to "Cancel", team_project_path(@project), class: "btn cancel-btn"
23 26  
24   -:css
25   - form select {
26   - width:300px;
27   - }
28 27  
29 28 :javascript
30   - $('select#team_member_user_id').chosen();
31   - $('select#team_member_project_access').chosen();
32   - //$('select#team_member_repo_access').chosen();
33   - //$('select#team_member_project_access').chosen();
  29 + $('select#user_ids').chosen();
  30 + $('select#project_access').chosen();
... ...
app/views/team_members/_show.html.haml
... ... @@ -2,12 +2,6 @@
2 2 - allow_admin = can? current_user, :admin_project, @project
3 3 %tr{id: dom_id(member), class: "team_member_row user_#{user.id}"}
4 4 %td
5   - .right
6   - - if @project.owner == user
7   - %span.label Project Owner
8   - - if user.blocked
9   - %span.label Blocked
10   -
11 5 = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
12 6 = image_tag gravatar_icon(user.email, 40), class: "avatar s32"
13 7 = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
... ... @@ -16,5 +10,11 @@
16 10 %div.cgray= user.email
17 11  
18 12 %td
19   - = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f|
20   - = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select", disabled: !allow_admin
  13 + .right
  14 + - if @project.owner == user
  15 + %span.btn.disabled.success Project Owner
  16 + - if user.blocked
  17 + %span.btn.disabled.blocked Blocked
  18 + - if allow_admin
  19 + = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f|
  20 + = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select"
... ...
app/views/wikis/_form.html.haml
... ... @@ -14,7 +14,7 @@
14 14 .middle_box_content
15 15 .input
16 16 %span.cgray
17   - Wiki content is parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}.
  17 + Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
18 18 To link to a (new) page you can just type
19 19 %code [Link Title](page-slug)
20 20 \.
... ...
doc/api/README.md
1   -# Gitlab API
  1 +# GitLab API
2 2  
3 3 All API requests require authentication. You need to pass a `private_token` parameter to authenticate. You can find or reset your private token in your profile.
4 4  
... ... @@ -10,7 +10,7 @@ If no, or an invalid, `private_token` is provided then an error message will be
10 10 }
11 11 ```
12 12  
13   -API requests should be prefixed with `api` and the API version. The API version is equal to the Gitlab major version number, which is defined in `lib/api.rb`.
  13 +API requests should be prefixed with `api` and the API version. The API version is equal to the GitLab major version number, which is defined in `lib/api.rb`.
14 14  
15 15 Example of a valid API request:
16 16  
... ...
doc/api/projects.md
... ... @@ -102,6 +102,12 @@ Parameters:
102 102 + `name` (required) - new project name
103 103 + `code` (optional) - new project code, uses project name if not set
104 104 + `path` (optional) - new project path, uses project name if not set
  105 ++ `description (optional) - short project description
  106 ++ `default_branch` (optional) - 'master' by default
  107 ++ `issues_enabled` (optional) - enabled by default
  108 ++ `wall_enabled` (optional) - enabled by default
  109 ++ `merge_requests_enabled` (optional) - enabled by default
  110 ++ `wiki_enabled` (optional) - enabled by default
105 111  
106 112 Will return created project with status `201 Created` on success, or `404 Not
107 113 found` on fail.
... ...
doc/installation.md
... ... @@ -167,7 +167,7 @@ and ensure you have followed all of the above steps carefully.
167 167 # Login to MySQL
168 168 $ mysql -u root -p
169 169  
170   - # Create the gitlabhq production database
  170 + # Create the GitLab production database
171 171 mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
172 172  
173 173 # Create the MySQL User change $password to a real password
... ...
features/projects/issues/issues.feature
... ... @@ -4,7 +4,7 @@ Feature: Issues
4 4 And I own project "Shop"
5 5 And project "Shop" have "Release 0.4" open issue
6 6 And project "Shop" have "Release 0.3" closed issue
7   - And I visit project "Shop" issues page
  7 + And I visit project "Shop" issues page
8 8  
9 9 Scenario: I should see open issues
10 10 Given I should see "Release 0.4" in issues
... ... @@ -36,3 +36,47 @@ Feature: Issues
36 36 Given I visit issue page "Release 0.4"
37 37 And I leave a comment like "XML attached"
38 38 Then I should see comment "XML attached"
  39 +
  40 + @javascript
  41 + Scenario: I search issue
  42 + Given I fill in issue search with "Release"
  43 + Then I should see "Release 0.4" in issues
  44 + And I should not see "Release 0.3" in issues
  45 +
  46 + @javascript
  47 + Scenario: I search issue that not exist
  48 + Given I fill in issue search with "Bug"
  49 + Then I should not see "Release 0.4" in issues
  50 + And I should not see "Release 0.3" in issues
  51 +
  52 +
  53 + @javascript
  54 + Scenario: I search all issues
  55 + Given I click link "All"
  56 + And I fill in issue search with "0.3"
  57 + Then I should see "Release 0.3" in issues
  58 + And I should not see "Release 0.4" in issues
  59 +
  60 + @javascript
  61 + Scenario: I clear search
  62 + Given I click link "All"
  63 + And I fill in issue search with "Something"
  64 + And I fill in issue search with ""
  65 + Then I should see "Release 0.4" in issues
  66 + And I should see "Release 0.3" in issues
  67 +
  68 + @javascript
  69 + Scenario: I create Issue with pre-selected milestone
  70 + Given project "Shop" has milestone "v2.2"
  71 + And project "Shop" has milestone "v3.0"
  72 + And I visit project "Shop" issues page
  73 + When I select milestone "v3.0"
  74 + And I click link "New Issue"
  75 + Then I should see selected milestone with title "v3.0"
  76 +
  77 + @javascript
  78 + Scenario: I create Issue with pre-selected assignee
  79 + When I select first assignee from "Shop" project
  80 + And I click link "New Issue"
  81 + Then I should see first assignee from "Shop" as selected assignee
  82 +
... ...
features/step_definitions/common_steps.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +include LoginHelpers
  2 +
  3 +Given /^I signin as a user$/ do
  4 + login_as :user
  5 +end
  6 +
  7 +When /^I click link "(.*?)"$/ do |link|
  8 + click_link link
  9 +end
  10 +
  11 +When /^I click button "(.*?)"$/ do |button|
  12 + click_button button
  13 +end
  14 +
  15 +When /^I fill in "(.*?)" with "(.*?)"$/ do |field, value|
  16 + fill_in field, :with => value
  17 +end
  18 +
  19 +Given /^show me page$/ do
  20 + save_and_open_page
  21 +end
... ...
features/step_definitions/dashboard_steps.rb
1   -Given /^I visit dashboard page$/ do
2   - visit dashboard_path
3   -end
4   -
5 1 Then /^I should see "(.*?)" link$/ do |arg1|
6 2 page.should have_link(arg1)
7 3 end
... ... @@ -51,10 +47,10 @@ Then /^I click &quot;(.*?)&quot; link$/ do |arg1|
51 47 end
52 48  
53 49 Then /^I see prefilled new Merge Request page$/ do
54   - current_path.should == new_project_merge_request_path(@project)
55   - find("#merge_request_source_branch").value.should == "new_design"
56   - find("#merge_request_target_branch").value.should == "master"
57   - find("#merge_request_title").value.should == "New Design"
  50 + current_path.should == new_project_merge_request_path(@project)
  51 + find("#merge_request_source_branch").value.should == "new_design"
  52 + find("#merge_request_target_branch").value.should == "master"
  53 + find("#merge_request_title").value.should == "New Design"
58 54 end
59 55  
60 56 Given /^I visit dashboard search page$/ do
... ... @@ -66,10 +62,6 @@ Given /^I search for &quot;(.*?)&quot;$/ do |arg1|
66 62 click_button "Search"
67 63 end
68 64  
69   -Given /^I visit dashboard issues page$/ do
70   - visit dashboard_issues_path
71   -end
72   -
73 65 Then /^I should see issues assigned to me$/ do
74 66 issues = @user.issues
75 67 issues.each do |issue|
... ... @@ -78,10 +70,6 @@ Then /^I should see issues assigned to me$/ do
78 70 end
79 71 end
80 72  
81   -Given /^I visit dashboard merge requests page$/ do
82   - visit dashboard_merge_requests_path
83   -end
84   -
85 73 Then /^I should see my merge requests$/ do
86 74 merge_requests = @user.merge_requests
87 75 merge_requests.each do |mr|
... ...
features/step_definitions/profile/profile_steps.rb
1   -Given /^I visit profile page$/ do
2   - visit profile_path
3   -end
4   -
5 1 Then /^I should see my profile info$/ do
6 2 page.should have_content "Profile"
7 3 page.should have_content @user.name
8 4 page.should have_content @user.email
9 5 end
10 6  
11   -Given /^I visit profile password page$/ do
12   - visit profile_password_path
13   -end
14   -
15 7 Then /^I change my password$/ do
16 8 fill_in "user_password", :with => "222333"
17 9 fill_in "user_password_confirmation", :with => "222333"
... ... @@ -22,10 +14,6 @@ Then /^I should be redirected to sign in page$/ do
22 14 current_path.should == new_user_session_path
23 15 end
24 16  
25   -Given /^I visit profile token page$/ do
26   - visit profile_token_path
27   -end
28   -
29 17 Then /^I reset my token$/ do
30 18 @old_token = @user.private_token
31 19 click_button "Reset"
... ...
features/step_definitions/project/browse_code_steps.rb
1   -Given /^I visit project source page$/ do
2   - visit tree_project_ref_path(@project, @project.root_ref)
3   -end
4   -
5 1 Then /^I should see files from repository$/ do
6 2 page.should have_content("app")
7 3 page.should have_content("History")
8 4 page.should have_content("Gemfile")
9 5 end
10 6  
11   -Given /^I visit project source page for "(.*?)"$/ do |arg1|
12   - visit tree_project_ref_path(@project, arg1)
13   -end
14   -
15 7 Then /^I should see files from repository for "(.*?)"$/ do |arg1|
16 8 current_path.should == tree_project_ref_path(@project, arg1)
17 9 page.should have_content("app")
... ... @@ -31,10 +23,6 @@ Given /^I click on raw button$/ do
31 23 click_link "raw"
32 24 end
33 25  
34   -Given /^I visit blob file from repo$/ do
35   - visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH)
36   -end
37   -
38 26 Then /^I should see raw file content$/ do
39 27 page.source.should == ValidCommit::BLOB_FILE
40 28 end
... ...
features/step_definitions/project/project_commits_steps.rb
1   -Given /^I visit project commits page$/ do
2   - visit project_commits_path(@project)
3   -end
4   -
5 1 Then /^I see project commits$/ do
6 2 current_path.should == project_commits_path(@project)
7 3  
... ... @@ -23,19 +19,11 @@ Then /^I see commits atom feed$/ do
23 19 page.body.should have_selector("entry summary", :text => commit.description)
24 20 end
25 21  
26   -Given /^I click on commit link$/ do
27   - visit project_commit_path(@project, ValidCommit::ID)
28   -end
29   -
30 22 Then /^I see commit info$/ do
31 23 page.should have_content ValidCommit::MESSAGE
32 24 page.should have_content "Showing 1 changed file"
33 25 end
34 26  
35   -Given /^I visit compare refs page$/ do
36   - visit compare_project_commits_path(@project)
37   -end
38   -
39 27 Given /^I fill compare fields with refs$/ do
40 28 fill_in "from", :with => "master"
41 29 fill_in "to", :with => "stable"
... ... @@ -48,18 +36,6 @@ Given /^I see compared refs$/ do
48 36 page.should have_content "Showing 73 changed files"
49 37 end
50 38  
51   -Given /^I visit project branches page$/ do
52   - visit branches_project_repository_path(@project)
53   -end
54   -
55   -Given /^I visit project commit page$/ do
56   - visit project_commit_path(@project, ValidCommit::ID)
57   -end
58   -
59   -Given /^I visit project tags page$/ do
60   - visit tags_project_repository_path(@project)
61   -end
62   -
63 39 Then /^I should see "(.*?)" recent branches list$/ do |arg1|
64 40 page.should have_content("Branches")
65 41 page.should have_content("master")
... ... @@ -76,7 +52,7 @@ Then /^I should see &quot;(.*?)&quot; all tags list$/ do |arg1|
76 52 end
77 53  
78 54 Then /^I should see "(.*?)" protected branches list$/ do |arg1|
79   - within "table" do
  55 + within "table" do
80 56 page.should have_content "stable"
81 57 page.should_not have_content "master"
82 58 end
... ...
features/step_definitions/project/project_issues_steps.rb
... ... @@ -8,16 +8,12 @@ Given /^project &quot;(.*?)&quot; have &quot;(.*?)&quot; closed issue$/ do |arg1, arg2|
8 8 Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first, :closed => true)
9 9 end
10 10  
11   -Given /^I visit project "(.*?)" issues page$/ do |arg1|
12   - visit project_issues_path(Project.find_by_name(arg1))
13   -end
14   -
15 11 Given /^I should see "(.*?)" in issues$/ do |arg1|
16   - page.should have_content arg1
  12 + page.should have_content arg1
17 13 end
18 14  
19 15 Given /^I should not see "(.*?)" in issues$/ do |arg1|
20   - page.should_not have_content arg1
  16 + page.should_not have_content arg1
21 17 end
22 18  
23 19 Then /^I should see issue "(.*?)"$/ do |arg1|
... ... @@ -27,11 +23,6 @@ Then /^I should see issue &quot;(.*?)&quot;$/ do |arg1|
27 23 page.should have_content issue.project.name
28 24 end
29 25  
30   -Given /^I visit issue page "(.*?)"$/ do |arg1|
31   - issue = Issue.find_by_title(arg1)
32   - visit project_issue_path(issue.project, issue)
33   -end
34   -
35 26 Given /^I submit new issue "(.*?)"$/ do |arg1|
36 27 fill_in "issue_title", with: arg1
37 28 click_button "Submit new issue"
... ... @@ -51,7 +42,40 @@ Given /^I visit project &quot;(.*?)&quot; labels page$/ do |arg1|
51 42 end
52 43  
53 44 Then /^I should see label "(.*?)"$/ do |arg1|
54   - within ".labels-table" do
  45 + within ".labels-table" do
55 46 page.should have_content arg1
56 47 end
57 48 end
  49 +
  50 +Given /^I fill in issue search with "(.*?)"$/ do |arg1|
  51 + # Because fill_in, with: "" triggers nothing
  52 + # we need to trigger a keyup event
  53 + if arg1 == ''
  54 + page.execute_script("$('.issue_search').val('').keyup();");
  55 + end
  56 + fill_in 'issue_search', with: arg1
  57 +end
  58 +
  59 +When /^I select milestone "(.*?)"$/ do |milestone_title|
  60 + select milestone_title, from: "milestone_id"
  61 +end
  62 +
  63 +Then /^I should see selected milestone with title "(.*?)"$/ do |milestone_title|
  64 + issues_milestone_selector = "#issue_milestone_id_chzn/a"
  65 + wait_until{ page.has_content?("Details") }
  66 + page.find(issues_milestone_selector).should have_content(milestone_title)
  67 +end
  68 +
  69 +When /^I select first assignee from "(.*?)" project$/ do |project_name|
  70 + project = Project.find_by_name project_name
  71 + first_assignee = project.users.first
  72 + select first_assignee.name, from: "assignee_id"
  73 +end
  74 +
  75 +Then /^I should see first assignee from "(.*?)" as selected assignee$/ do |project_name|
  76 + issues_assignee_selector = "#issue_assignee_id_chzn/a"
  77 + wait_until{ page.has_content?("Details") }
  78 + project = Project.find_by_name project_name
  79 + assignee_name = project.users.first.name
  80 + page.find(issues_assignee_selector).should have_content(assignee_name)
  81 +end
... ...
features/step_definitions/project/project_merge_requests_steps.rb
... ... @@ -8,21 +8,17 @@ Given /^project &quot;(.*?)&quot; have &quot;(.*?)&quot; closed merge request$/ do |arg1, arg2|
8 8 Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first, :closed => true)
9 9 end
10 10  
11   -Given /^I visit project "(.*?)" merge requests page$/ do |arg1|
12   - visit project_merge_requests_path(Project.find_by_name(arg1))
13   -end
14   -
15 11 Then /^I should see "(.*?)" in merge requests$/ do |arg1|
16   - page.should have_content arg1
  12 + page.should have_content arg1
17 13 end
18 14  
19 15 Then /^I should not see "(.*?)" in merge requests$/ do |arg1|
20   - page.should_not have_content arg1
  16 + page.should_not have_content arg1
21 17 end
22 18  
23 19 Then /^I should see merge request "(.*?)"$/ do |arg1|
24 20 merge_request = MergeRequest.find_by_title(arg1)
25   - page.should have_content(merge_request.title[0..10])
  21 + page.should have_content(merge_request.title[0..10])
26 22 page.should have_content(merge_request.target_branch)
27 23 page.should have_content(merge_request.source_branch)
28 24 end
... ... @@ -34,11 +30,6 @@ Given /^I submit new merge request &quot;(.*?)&quot;$/ do |arg1|
34 30 click_button "Save"
35 31 end
36 32  
37   -Given /^I visit merge request page "(.*?)"$/ do |arg1|
38   - mr = MergeRequest.find_by_title(arg1)
39   - visit project_merge_request_path(mr.project, mr)
40   -end
41   -
42 33 Then /^I should see closed merge request "(.*?)"$/ do |arg1|
43 34 mr = MergeRequest.find_by_title(arg1)
44 35 mr.closed.should be_true
... ...
features/step_definitions/project/project_milestones_steps.rb
... ... @@ -12,11 +12,6 @@ Given /^project &quot;(.*?)&quot; has milestone &quot;(.*?)&quot;$/ do |arg1, arg2|
12 12 end
13 13 end
14 14  
15   -Given /^I visit project "(.*?)" milestones page$/ do |arg1|
16   - @project = Project.find_by_name(arg1)
17   - visit project_milestones_path(@project)
18   -end
19   -
20 15 Then /^I should see active milestones$/ do
21 16 milestone = @project.milestones.first
22 17 page.should have_content(milestone.title[0..10])
... ...
features/step_definitions/project/project_team_steps.rb
... ... @@ -8,10 +8,6 @@ Given /^&quot;(.*?)&quot; is &quot;(.*?)&quot; developer$/ do |arg1, arg2|
8 8 project.add_access(user, :write)
9 9 end
10 10  
11   -Given /^I visit project "(.*?)" team page$/ do |arg1|
12   - visit team_project_path(Project.find_by_name(arg1))
13   -end
14   -
15 11 Then /^I should be able to see myself in team$/ do
16 12 page.should have_content(@user.name)
17 13 page.should have_content(@user.email)
... ... @@ -23,15 +19,11 @@ Then /^I should see &quot;(.*?)&quot; in team list$/ do |arg1|
23 19 page.should have_content(user.email)
24 20 end
25 21  
26   -Given /^I click link "(.*?)"$/ do |arg1|
27   - click_link arg1
28   -end
29   -
30 22 Given /^I select "(.*?)" as "(.*?)"$/ do |arg1, arg2|
31 23 user = User.find_by_name(arg1)
32   - within "#new_team_member" do
33   - select user.name, :from => "team_member_user_id"
34   - select arg2, :from => "team_member_project_access"
  24 + within "#new_team_member" do
  25 + select user.name, :from => "user_ids"
  26 + select arg2, :from => "project_access"
35 27 end
36 28 click_button "Save"
37 29 end
... ... @@ -44,7 +36,7 @@ end
44 36  
45 37 Given /^I change "(.*?)" role to "(.*?)"$/ do |arg1, arg2|
46 38 user = User.find_by_name(arg1)
47   - within ".user_#{user.id}" do
  39 + within ".user_#{user.id}" do
48 40 select arg2, :from => "team_member_project_access"
49 41 end
50 42 end
... ...
features/step_definitions/project/project_wiki_steps.rb
1   -Given /^I visit project wiki page$/ do
2   - visit project_wiki_path(@project, :index)
3   -end
4   -
5 1 Given /^I create Wiki page$/ do
6 2 fill_in "Title", :with => 'Test title'
7 3 fill_in "Content", :with => '[link test](test)'
... ...
features/step_definitions/project/projects_steps.rb
1   -include LoginHelpers
2   -
3   -Given /^I signin as a user$/ do
4   - login_as :user
5   -end
6   -
7 1 When /^I visit new project page$/ do
8 2 visit new_project_path
9 3 end
... ... @@ -65,10 +59,6 @@ Given /^I visit project &quot;(.*?)&quot; network page$/ do |arg1|
65 59 visit graph_project_path(project)
66 60 end
67 61  
68   -Given /^show me page$/ do
69   - save_and_open_page
70   -end
71   -
72 62 Given /^page should have network graph$/ do
73 63 page.should have_content "Project Network Graph"
74 64 within ".graph" do
... ...
features/step_definitions/visit_steps.rb 0 → 100644
... ... @@ -0,0 +1,91 @@
  1 +Given /^I visit project "(.*?)" issues page$/ do |arg1|
  2 + visit project_issues_path(Project.find_by_name(arg1))
  3 +end
  4 +
  5 +Given /^I visit issue page "(.*?)"$/ do |arg1|
  6 + issue = Issue.find_by_title(arg1)
  7 + visit project_issue_path(issue.project, issue)
  8 +end
  9 +
  10 +Given /^I visit project "(.*?)" merge requests page$/ do |arg1|
  11 + visit project_merge_requests_path(Project.find_by_name(arg1))
  12 +end
  13 +
  14 +Given /^I visit merge request page "(.*?)"$/ do |arg1|
  15 + mr = MergeRequest.find_by_title(arg1)
  16 + visit project_merge_request_path(mr.project, mr)
  17 +end
  18 +
  19 +Given /^I visit project "(.*?)" milestones page$/ do |arg1|
  20 + @project = Project.find_by_name(arg1)
  21 + visit project_milestones_path(@project)
  22 +end
  23 +
  24 +Given /^I visit project commits page$/ do
  25 + visit project_commits_path(@project)
  26 +end
  27 +
  28 +Given /^I visit compare refs page$/ do
  29 + visit compare_project_commits_path(@project)
  30 +end
  31 +
  32 +Given /^I visit project branches page$/ do
  33 + visit branches_project_repository_path(@project)
  34 +end
  35 +
  36 +Given /^I visit project commit page$/ do
  37 + visit project_commit_path(@project, ValidCommit::ID)
  38 +end
  39 +
  40 +Given /^I visit project tags page$/ do
  41 + visit tags_project_repository_path(@project)
  42 +end
  43 +
  44 +Given /^I click on commit link$/ do
  45 + visit project_commit_path(@project, ValidCommit::ID)
  46 +end
  47 +
  48 +Given /^I visit project source page$/ do
  49 + visit tree_project_ref_path(@project, @project.root_ref)
  50 +end
  51 +
  52 +Given /^I visit project source page for "(.*?)"$/ do |arg1|
  53 + visit tree_project_ref_path(@project, arg1)
  54 +end
  55 +
  56 +Given /^I visit blob file from repo$/ do
  57 + visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH)
  58 +end
  59 +
  60 +Given /^I visit project "(.*?)" team page$/ do |arg1|
  61 + visit team_project_path(Project.find_by_name(arg1))
  62 +end
  63 +
  64 +Given /^I visit project wiki page$/ do
  65 + visit project_wiki_path(@project, :index)
  66 +end
  67 +
  68 +Given /^I visit profile page$/ do
  69 + visit profile_path
  70 +end
  71 +
  72 +Given /^I visit profile token page$/ do
  73 + visit profile_token_path
  74 +end
  75 +
  76 +Given /^I visit profile password page$/ do
  77 + visit profile_password_path
  78 +end
  79 +
  80 +Given /^I visit dashboard page$/ do
  81 + visit dashboard_path
  82 +end
  83 +
  84 +Given /^I visit dashboard issues page$/ do
  85 + visit dashboard_issues_path
  86 +end
  87 +
  88 +Given /^I visit dashboard merge requests page$/ do
  89 + visit dashboard_merge_requests_path
  90 +end
  91 +
... ...
lib/api/projects.rb
... ... @@ -29,14 +29,24 @@ module Gitlab
29 29 # name (required) - name for new project
30 30 # code (optional) - code for new project, uses project name if not set
31 31 # path (optional) - path for new project, uses project name if not set
  32 + # description (optional) - short project description
  33 + # default_branch (optional) - 'master' by default
  34 + # issues_enabled (optional) - enabled by default
  35 + # wall_enabled (optional) - enabled by default
  36 + # merge_requests_enabled (optional) - enabled by default
  37 + # wiki_enabled (optional) - enabled by default
32 38 # Example Request
33 39 # POST /projects
34 40 post do
35   - project = {}
36   - project[:name] = params[:name]
37   - project[:code] = params[:code] || project[:name]
38   - project[:path] = params[:path] || project[:name]
39   - @project = Project.create_by_user(project, current_user)
  41 + params[:code] ||= params[:name]
  42 + params[:path] ||= params[:name]
  43 + project_attrs = {}
  44 + params.each_pair do |k ,v|
  45 + if Project.attribute_names.include? k
  46 + project_attrs[k] = v
  47 + end
  48 + end
  49 + @project = Project.create_by_user(project_attrs, current_user)
40 50 if @project.saved?
41 51 present @project, with: Entities::Project
42 52 else
... ...
lib/gitlab/backend/gitolite.rb
1   -require 'gitolite'
2   -require 'timeout'
3   -require 'fileutils'
  1 +require_relative 'gitolite_config'
4 2  
5   -# TODO: refactor & cleanup
6 3 module Gitlab
7 4 class Gitolite
8 5 class AccessDenied < StandardError; end
9   - class InvalidKey < StandardError; end
  6 +
  7 + def config
  8 + Gitlab::GitoliteConfig.new
  9 + end
10 10  
11 11 def set_key key_id, key_content, projects
12   - configure do |c|
13   - c.update_keys(key_id, key_content)
14   - c.update_projects(projects)
  12 + config.apply do |config|
  13 + config.write_key(key_id, key_content)
  14 + config.update_projects(projects)
15 15 end
16 16 end
17 17  
18 18 def remove_key key_id, projects
19   - configure do |c|
20   - c.delete_key(key_id)
21   - c.update_projects(projects)
  19 + config.apply do |config|
  20 + config.rm_key(key_id)
  21 + config.update_projects(projects)
22 22 end
23 23 end
24 24  
25 25 def update_repository project
26   - configure do |c|
27   - c.update_project(project.path, project)
28   - end
  26 + config.update_project!(project.path, project)
29 27 end
30 28  
31   - alias_method :create_repository, :update_repository
32   -
33 29 def remove_repository project
34   - configure do |c|
35   - c.destroy_project(project)
36   - end
  30 + config.destroy_project!(project)
37 31 end
38 32  
39 33 def url_to_repo path
40 34 Gitlab.config.ssh_path + "#{path}.git"
41 35 end
42 36  
43   - def initialize
44   - # create tmp dir
45   - @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
46   - end
47   -
48 37 def enable_automerge
49   - configure do |git|
50   - git.admin_all_repo
51   - end
52   - end
53   -
54   - protected
55   -
56   - def destroy_project(project)
57   - FileUtils.rm_rf(project.path_to_repo)
58   -
59   - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
60   - conf = ga_repo.config
61   - conf.rm_repo(project.path)
62   - ga_repo.save
63   - end
64   -
65   - #update or create
66   - def update_keys(user, key)
67   - File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) }
68   - end
69   -
70   - def delete_key(user)
71   - File.unlink(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"))
72   - `cd #{File.join(@local_dir,'gitolite')} ; git rm keydir/#{user}.pub`
73   - end
74   -
75   - # update or create
76   - def update_project(repo_name, project)
77   - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
78   - conf = ga_repo.config
79   - repo = update_project_config(project, conf)
80   - conf.add_repo(repo, true)
81   -
82   - ga_repo.save
83   - end
84   -
85   - # Updates many projects and uses project.path as the repo path
86   - # An order of magnitude faster than update_project
87   - def update_projects(projects)
88   - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
89   - conf = ga_repo.config
90   -
91   - projects.each do |project|
92   - repo = update_project_config(project, conf)
93   - conf.add_repo(repo, true)
94   - end
95   -
96   - ga_repo.save
97   - end
98   -
99   - def update_project_config(project, conf)
100   - repo_name = project.path
101   -
102   - repo = if conf.has_repo?(repo_name)
103   - conf.get_repo(repo_name)
104   - else
105   - ::Gitolite::Config::Repo.new(repo_name)
106   - end
107   -
108   - name_readers = project.repository_readers
109   - name_writers = project.repository_writers
110   - name_masters = project.repository_masters
111   -
112   - pr_br = project.protected_branches.map(&:name).join("$ ")
113   -
114   - repo.clean_permissions
115   -
116   - # Deny access to protected branches for writers
117   - unless name_writers.blank? || pr_br.blank?
118   - repo.add_permission("-", pr_br.strip + "$ ", name_writers)
119   - end
120   -
121   - # Add read permissions
122   - repo.add_permission("R", "", name_readers) unless name_readers.blank?
123   -
124   - # Add write permissions
125   - repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
126   - repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
127   -
128   - repo
129   - end
130   -
131   - def admin_all_repo
132   - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
133   - conf = ga_repo.config
134   - owner_name = ""
135   -
136   - # Read gitolite-admin user
137   - #
138   - begin
139   - repo = conf.get_repo("gitolite-admin")
140   - owner_name = repo.permissions[0]["RW+"][""][0]
141   - raise StandardError if owner_name.blank?
142   - rescue => ex
143   - puts "Can't determine gitolite-admin owner".red
144   - raise StandardError
145   - end
146   -
147   - # @ALL repos premission for gitolite owner
148   - repo_name = "@all"
149   - repo = if conf.has_repo?(repo_name)
150   - conf.get_repo(repo_name)
151   - else
152   - ::Gitolite::Config::Repo.new(repo_name)
153   - end
154   -
155   - repo.add_permission("RW+", "", owner_name)
156   - conf.add_repo(repo, true)
157   - ga_repo.save
  38 + config.admin_all_repo!(project)
158 39 end
159 40  
160   - private
161   -
162   - def pull
163   - # create tmp dir
164   - @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
165   - Dir.mkdir @local_dir
166   -
167   - `git clone #{Gitlab.config.gitolite_admin_uri} #{@local_dir}/gitolite`
168   - end
169   -
170   - def push
171   - Dir.chdir(File.join(@local_dir, "gitolite"))
172   - `git add -A`
173   - `git commit -am "Gitlab"`
174   - `git push`
175   - Dir.chdir(Rails.root)
176   -
177   - FileUtils.rm_rf(@local_dir)
178   - end
179   -
180   - def configure
181   - Timeout::timeout(30) do
182   - File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
183   - begin
184   - f.flock(File::LOCK_EX)
185   - pull
186   - yield(self)
187   - push
188   - ensure
189   - f.flock(File::LOCK_UN)
190   - end
191   - end
192   - end
193   - rescue Exception => ex
194   - if ex.message =~ /is not a valid SSH key string/
195   - raise Gitolite::InvalidKey.new("ssh key is not valid")
196   - else
197   - Gitlab::Logger.error(ex.message)
198   - raise Gitolite::AccessDenied.new("gitolite timeout")
199   - end
200   - end
  41 + alias_method :create_repository, :update_repository
201 42 end
202 43 end
... ...
lib/gitlab/backend/gitolite_config.rb 0 → 100644
... ... @@ -0,0 +1,203 @@
  1 +require 'gitolite'
  2 +require 'timeout'
  3 +require 'fileutils'
  4 +
  5 +module Gitlab
  6 + class GitoliteConfig
  7 + class PullError < StandardError; end
  8 + class PushError < StandardError; end
  9 +
  10 + attr_reader :config_tmp_dir, :ga_repo, :conf
  11 +
  12 + def config_tmp_dir
  13 + @config_tmp_dir ||= File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
  14 + end
  15 +
  16 + def ga_repo
  17 + @ga_repo ||= ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
  18 + end
  19 +
  20 + def apply
  21 + Timeout::timeout(30) do
  22 + File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
  23 + begin
  24 + # Set exclusive lock
  25 + # to prevent race condition
  26 + f.flock(File::LOCK_EX)
  27 +
  28 + # Pull gitolite-admin repo
  29 + # in tmp dir before do any changes
  30 + pull(config_tmp_dir)
  31 +
  32 + # Build ga_repo object and @conf
  33 + # to access gitolite-admin configuration
  34 + @conf = ga_repo.config
  35 +
  36 + # Do any changes
  37 + # in gitolite-admin
  38 + # config here
  39 + yield(self)
  40 +
  41 + # Save changes in
  42 + # gitolite-admin repo
  43 + # before pusht it
  44 + ga_repo.save
  45 +
  46 + # Push gitolite-admin repo
  47 + # to apply all changes
  48 + push(config_tmp_dir)
  49 +
  50 + # Remove tmp dir
  51 + # wiith gitolite-admin
  52 + FileUtils.rm_rf(config_tmp_dir)
  53 + ensure
  54 + # unlock so other task cann access
  55 + # gitolite configuration
  56 + f.flock(File::LOCK_UN)
  57 + end
  58 + end
  59 + end
  60 + rescue PullError => ex
  61 + Gitlab::Logger.error("Pull error -> " + ex.message)
  62 + raise Gitolite::AccessDenied, ex.message
  63 +
  64 + rescue PushError => ex
  65 + Gitlab::Logger.error("Push error -> " + " " + ex.message)
  66 + raise Gitolite::AccessDenied, ex.message
  67 +
  68 + rescue Exception => ex
  69 + Gitlab::Logger.error(ex.class.name + " " + ex.message)
  70 + raise Gitolite::AccessDenied.new("gitolite timeout")
  71 + end
  72 +
  73 + def destroy_project(project)
  74 + FileUtils.rm_rf(project.path_to_repo)
  75 + conf.rm_repo(project.path)
  76 + end
  77 +
  78 + def destroy_project!(project)
  79 + apply do |config|
  80 + config.destroy_project(project)
  81 + end
  82 + end
  83 +
  84 + def write_key(id, key)
  85 + File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f|
  86 + f.write(key.gsub(/\n/,''))
  87 + end
  88 + end
  89 +
  90 + def rm_key(user)
  91 + File.unlink(File.join(config_tmp_dir, 'gitolite/keydir',"#{user}.pub"))
  92 + `cd #{File.join(config_tmp_dir,'gitolite')} ; git rm keydir/#{user}.pub`
  93 + end
  94 +
  95 + # update or create
  96 + def update_project(repo_name, project)
  97 + repo = update_project_config(project, conf)
  98 + conf.add_repo(repo, true)
  99 + end
  100 +
  101 + def update_project!(repo_name, project)
  102 + apply do |config|
  103 + config.update_project(repo_name, project)
  104 + end
  105 + end
  106 +
  107 + # Updates many projects and uses project.path as the repo path
  108 + # An order of magnitude faster than update_project
  109 + def update_projects(projects)
  110 + projects.each do |project|
  111 + repo = update_project_config(project, conf)
  112 + conf.add_repo(repo, true)
  113 + end
  114 + end
  115 +
  116 + def update_project_config(project, conf)
  117 + repo_name = project.path
  118 +
  119 + repo = if conf.has_repo?(repo_name)
  120 + conf.get_repo(repo_name)
  121 + else
  122 + ::Gitolite::Config::Repo.new(repo_name)
  123 + end
  124 +
  125 + name_readers = project.repository_readers
  126 + name_writers = project.repository_writers
  127 + name_masters = project.repository_masters
  128 +
  129 + pr_br = project.protected_branches.map(&:name).join("$ ")
  130 +
  131 + repo.clean_permissions
  132 +
  133 + # Deny access to protected branches for writers
  134 + unless name_writers.blank? || pr_br.blank?
  135 + repo.add_permission("-", pr_br.strip + "$ ", name_writers)
  136 + end
  137 +
  138 + # Add read permissions
  139 + repo.add_permission("R", "", name_readers) unless name_readers.blank?
  140 +
  141 + # Add write permissions
  142 + repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
  143 + repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
  144 +
  145 + repo
  146 + end
  147 +
  148 + # Enable access to all repos for gitolite admin.
  149 + # We use it for accept merge request feature
  150 + def admin_all_repo
  151 + owner_name = ""
  152 +
  153 + # Read gitolite-admin user
  154 + #
  155 + begin
  156 + repo = conf.get_repo("gitolite-admin")
  157 + owner_name = repo.permissions[0]["RW+"][""][0]
  158 + raise StandardError if owner_name.blank?
  159 + rescue => ex
  160 + puts "Can't determine gitolite-admin owner".red
  161 + raise StandardError
  162 + end
  163 +
  164 + # @ALL repos premission for gitolite owner
  165 + repo_name = "@all"
  166 + repo = if conf.has_repo?(repo_name)
  167 + conf.get_repo(repo_name)
  168 + else
  169 + ::Gitolite::Config::Repo.new(repo_name)
  170 + end
  171 +
  172 + repo.add_permission("RW+", "", owner_name)
  173 + conf.add_repo(repo, true)
  174 + end
  175 +
  176 + def admin_all_repo!
  177 + apply { |config| config.admin_all_repo }
  178 + end
  179 +
  180 + private
  181 +
  182 + def pull tmp_dir
  183 + Dir.mkdir tmp_dir
  184 + `git clone #{Gitlab.config.gitolite_admin_uri} #{tmp_dir}/gitolite`
  185 +
  186 + unless File.exists?(File.join(tmp_dir, 'gitolite', 'conf', 'gitolite.conf'))
  187 + raise PullError, "unable to clone gitolite-admin repo"
  188 + end
  189 + end
  190 +
  191 + def push tmp_dir
  192 + Dir.chdir(File.join(tmp_dir, "gitolite"))
  193 + system('git add -A')
  194 + system('git commit -am "GitLab"')
  195 + if system('git push')
  196 + Dir.chdir(Rails.root)
  197 + else
  198 + raise PushError, "unable to push gitolite-admin repo"
  199 + end
  200 + end
  201 + end
  202 +end
  203 +
... ...
lib/gitlab/markdown.rb
1 1 module Gitlab
2   - # Custom parser for Gitlab-flavored Markdown
  2 + # Custom parser for GitLab-flavored Markdown
3 3 #
4   - # It replaces references in the text with links to the appropriate items in Gitlab.
  4 + # It replaces references in the text with links to the appropriate items in
  5 + # GitLab.
5 6 #
6 7 # Supported reference formats are:
7 8 # * @foo for team members
... ... @@ -10,19 +11,20 @@ module Gitlab
10 11 # * $123 for snippets
11 12 # * 123456 for commits
12 13 #
13   - # Examples
  14 + # It also parses Emoji codes to insert images. See
  15 + # http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
14 16 #
15   - # >> m = Markdown.new(...)
  17 + # Examples
16 18 #
17   - # >> m.parse("Hey @david, can you fix this?")
  19 + # >> gfm("Hey @david, can you fix this?")
18 20 # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
19 21 #
20   - # >> m.parse("Commit 35d5f7c closes #1234")
  22 + # >> gfm("Commit 35d5f7c closes #1234")
21 23 # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
22   - class Markdown
23   - include Rails.application.routes.url_helpers
24   - include ActionView::Helpers
25   -
  24 + #
  25 + # >> gfm(":trollface:")
  26 + # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
  27 + module Markdown
26 28 REFERENCE_PATTERN = %r{
27 29 ([^\w&;])? # Prefix (1)
28 30 ( # Reference (2)
... ... @@ -33,15 +35,57 @@ module Gitlab
33 35 ([^\w&;])? # Suffix (6)
34 36 }x.freeze
35 37  
  38 + EMOJI_PATTERN = %r{(:(\S+):)}.freeze
  39 +
36 40 attr_reader :html_options
37 41  
38   - def initialize(project, html_options = {})
39   - @project = project
  42 + # Public: Parse the provided text with GitLab-Flavored Markdown
  43 + #
  44 + # text - the source text
  45 + # html_options - extra options for the reference links as given to link_to
  46 + #
  47 + # Note: reference links will only be generated if @project is set
  48 + def gfm(text, html_options = {})
  49 + return text if text.nil?
  50 +
  51 + # prevents the string supplied through the _text_ argument to be altered
  52 + text = text.dup
  53 +
40 54 @html_options = html_options
  55 +
  56 + # Extract pre blocks so they are not altered
  57 + # from http://github.github.com/github-flavored-markdown/
  58 + extractions = {}
  59 + text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
  60 + md5 = Digest::MD5.hexdigest(match)
  61 + extractions[md5] = match
  62 + "{gfm-extraction-#{md5}}"
  63 + end
  64 +
  65 + # TODO: add popups with additional information
  66 +
  67 + text = parse(text)
  68 +
  69 + # Insert pre block extractions
  70 + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
  71 + extractions[$1]
  72 + end
  73 +
  74 + sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
41 75 end
42 76  
  77 + private
  78 +
  79 + # Private: Parses text for references and emoji
  80 + #
  81 + # text - Text to parse
  82 + #
  83 + # Note: reference links will only be generated if @project is set
  84 + #
  85 + # Returns parsed text
43 86 def parse(text)
44   - text.gsub(REFERENCE_PATTERN) do |match|
  87 + # parse reference links
  88 + text.gsub!(REFERENCE_PATTERN) do |match|
45 89 prefix = $1 || ''
46 90 reference = $2
47 91 identifier = $3 || $4 || $5
... ... @@ -52,10 +96,28 @@ module Gitlab
52 96 else
53 97 match
54 98 end
  99 + end if @project
  100 +
  101 + # parse emoji
  102 + text.gsub!(EMOJI_PATTERN) do |match|
  103 + if valid_emoji?($2)
  104 + image_tag("emoji/#{$2}.png", size: "20x20", class: 'emoji', title: $1, alt: $1)
  105 + else
  106 + match
  107 + end
55 108 end
  109 +
  110 + text
56 111 end
57 112  
58   - private
  113 + # Private: Checks if an emoji icon exists in the image asset directory
  114 + #
  115 + # emoji - Identifier of the emoji as a string (e.g., "+1", "heart")
  116 + #
  117 + # Returns boolean
  118 + def valid_emoji?(emoji)
  119 + File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png"))
  120 + end
59 121  
60 122 # Private: Dispatches to a dedicated processing method based on reference
61 123 #
... ...
lib/hooks/post-receive
1 1 #!/usr/bin/env bash
2 2  
3   -# This file was placed here by Gitlab. It makes sure that your pushed commits
  3 +# This file was placed here by GitLab. It makes sure that your pushed commits
4 4 # will be processed properly.
5 5  
6 6 while read oldrev newrev ref
... ...
lib/tasks/bulk_import.rake
1   -IMPORT_DIRECTORY = 'import_projects'
2   -
3   -desc "Imports existing Git repos into new projects from the import_projects folder"
4   -task :import_projects, [:email] => :environment do |t, args|
5   - REPOSITORY_DIRECTORY = Gitlab.config.git_base_path
6 1  
  2 +desc "Imports existing Git repos from a directory into new projects in git_base_path"
  3 +task :import_projects, [:directory,:email] => :environment do |t, args|
7 4 user_email = args.email
8   - repos_to_import = Dir.glob("#{IMPORT_DIRECTORY}/*")
9   -
  5 + import_directory = args.directory
  6 + repos_to_import = Dir.glob("#{import_directory}/*")
  7 + git_base_path = Gitlab.config.git_base_path
10 8 puts "Found #{repos_to_import.length} repos to import"
11 9  
12 10 imported_count = 0
... ... @@ -14,11 +12,9 @@ task :import_projects, [:email] =&gt; :environment do |t, args|
14 12 failed_count = 0
15 13 repos_to_import.each do |repo_path|
16 14 repo_name = File.basename repo_path
17   - repo_full_path = File.join(Rails.root, repo_path)
18 15  
19 16 puts " Processing #{repo_name}"
20   -
21   - clone_path = "#{REPOSITORY_DIRECTORY}/#{repo_name}.git"
  17 + clone_path = "#{git_base_path}#{repo_name}.git"
22 18  
23 19 if Dir.exists? clone_path
24 20 if Project.find_by_code(repo_name)
... ... @@ -30,7 +26,7 @@ task :import_projects, [:email] =&gt; :environment do |t, args|
30 26 end
31 27 else
32 28 # Clone the repo
33   - unless clone_bare_repo_as_git(repo_full_path, clone_path)
  29 + unless clone_bare_repo_as_git(repo_path, clone_path)
34 30 failed_count += 1
35 31 next
36 32 end
... ... @@ -48,14 +44,17 @@ task :import_projects, [:email] =&gt; :environment do |t, args|
48 44 puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})."
49 45 end
50 46  
51   -# Clones a repo as bare git repo using the git user
  47 +# Clones a repo as bare git repo using the git_user
52 48 def clone_bare_repo_as_git(existing_path, new_path)
  49 + git_user = Gitlab.config.ssh_user
53 50 begin
54   - sh "sudo -u git -i git clone --bare '#{existing_path}' #{new_path}"
  51 + sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}"
55 52 true
56   - rescue
  53 + rescue Exception=> msg
57 54 puts " ERROR: Faild to clone #{existing_path} to #{new_path}"
58   - false
  55 + puts " Make sure #{git_user} can reach #{existing_path}"
  56 + puts " Exception-MSG: #{msg}"
  57 + false
59 58 end
60 59 end
61 60  
... ...
lib/tasks/gitlab/write_hook.rake
1 1 namespace :gitlab do
2 2 namespace :gitolite do
3   - desc "GITLAB | Write GITLAB hook for gitolite"
  3 + desc "GITLAB | Write GitLab hook for gitolite"
4 4 task :write_hooks => :environment do
5 5 gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common")
6 6 gitlab_hooks_path = Rails.root.join("lib", "hooks")
... ...
spec/factories.rb
... ... @@ -11,6 +11,9 @@ module Factory
11 11 def self.new(type, *args)
12 12 FactoryGirl.build(type, *args)
13 13 end
  14 + def self.attributes(type, *args)
  15 + FactoryGirl.attributes_for(type, *args)
  16 + end
14 17 end
15 18  
16 19 FactoryGirl.define do
... ...
spec/factories_spec.rb
1 1 require 'spec_helper'
2 2  
3   -describe "Factories" do
4   - describe 'User' do
5   - it "builds a valid instance" do
6   - build(:user).should be_valid
7   - end
8   -
9   - it "builds a valid admin instance" do
10   - build(:admin).should be_valid
11   - end
12   - end
13   -
14   - describe 'Project' do
15   - it "builds a valid instance" do
16   - build(:project).should be_valid
17   - end
18   - end
19   -
20   - describe 'Issue' do
21   - it "builds a valid instance" do
22   - build(:issue).should be_valid
23   - end
24   -
25   - it "builds a valid closed instance" do
26   - build(:closed_issue).should be_valid
27   - end
28   - end
29   -
30   - describe 'MergeRequest' do
31   - it "builds a valid instance" do
32   - build(:merge_request).should be_valid
33   - end
34   - end
35   -
36   - describe 'Note' do
37   - it "builds a valid instance" do
38   - build(:note).should be_valid
39   - end
40   - end
41   -
42   - describe 'Event' do
43   - it "builds a valid instance" do
44   - build(:event).should be_valid
45   - end
46   - end
47   -
48   - describe 'Key' do
49   - it "builds a valid instance" do
50   - build(:key).should be_valid
51   - end
52   -
53   - it "builds a valid deploy key instance" do
54   - build(:deploy_key).should be_valid
55   - end
56   -
57   - it "builds a valid personal key instance" do
58   - build(:personal_key).should be_valid
59   - end
60   - end
61   -
62   - describe 'Milestone' do
63   - it "builds a valid instance" do
64   - build(:milestone).should be_valid
65   - end
66   - end
67   -
68   - describe 'SystemHook' do
69   - it "builds a valid instance" do
70   - build(:system_hook).should be_valid
71   - end
72   - end
73   -
74   - describe 'ProjectHook' do
75   - it "builds a valid instance" do
76   - build(:project_hook).should be_valid
77   - end
78   - end
79   -
80   - describe 'Wiki' do
81   - it "builds a valid instance" do
82   - build(:wiki).should be_valid
83   - end
84   - end
85   -
86   - describe 'Snippet' do
87   - it "builds a valid instance" do
88   - build(:snippet).should be_valid
  3 +FactoryGirl.factories.map(&:name).each do |factory_name|
  4 + describe "#{factory_name} factory" do
  5 + it 'should be valid' do
  6 + build(factory_name).should be_valid
89 7 end
90 8 end
91 9 end
... ...
spec/helpers/gitlab_markdown_helper_spec.rb
... ... @@ -208,6 +208,51 @@ describe GitlabMarkdownHelper do
208 208 gfm(actual).should match(expected)
209 209 end
210 210 end
  211 +
  212 + describe "emoji" do
  213 + it "matches at the start of a string" do
  214 + gfm(":+1:").should match(/<img/)
  215 + end
  216 +
  217 + it "matches at the end of a string" do
  218 + gfm("This gets a :-1:").should match(/<img/)
  219 + end
  220 +
  221 + it "matches with adjacent text" do
  222 + gfm("+1 (:+1:)").should match(/<img/)
  223 + end
  224 +
  225 + it "has a title attribute" do
  226 + gfm(":-1:").should match(/title=":-1:"/)
  227 + end
  228 +
  229 + it "has an alt attribute" do
  230 + gfm(":-1:").should match(/alt=":-1:"/)
  231 + end
  232 +
  233 + it "has an emoji class" do
  234 + gfm(":+1:").should match('class="emoji"')
  235 + end
  236 +
  237 + it "sets height and width" do
  238 + actual = gfm(":+1:")
  239 + actual.should match(/width="20"/)
  240 + actual.should match(/height="20"/)
  241 + end
  242 +
  243 + it "keeps whitespace intact" do
  244 + gfm("This deserves a :+1: big time.").should match(/deserves a <img.+\/> big time/)
  245 + end
  246 +
  247 + it "ignores invalid emoji" do
  248 + gfm(":invalid-emoji:").should_not match(/<img/)
  249 + end
  250 +
  251 + it "should work independet of reference links (i.e. without @project being set)" do
  252 + @project = nil
  253 + gfm(":+1:").should match(/<img/)
  254 + end
  255 + end
211 256 end
212 257  
213 258 describe "#link_to_gfm" do
... ...
spec/lib/gitolite_config_spec.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::GitoliteConfig do
  4 + let(:gitolite) { Gitlab::GitoliteConfig.new }
  5 +
  6 + it { should respond_to :write_key }
  7 + it { should respond_to :rm_key }
  8 + it { should respond_to :update_project }
  9 + it { should respond_to :update_project! }
  10 + it { should respond_to :update_projects }
  11 + it { should respond_to :destroy_project }
  12 + it { should respond_to :destroy_project! }
  13 + it { should respond_to :apply }
  14 + it { should respond_to :admin_all_repo }
  15 + it { should respond_to :admin_all_repo! }
  16 +end
... ...
spec/lib/gitolite_spec.rb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::Gitolite do
  4 + let(:project) { double('Project', path: 'diaspora') }
  5 + let(:gitolite_config) { double('Gitlab::GitoliteConfig') }
  6 + let(:gitolite) { Gitlab::Gitolite.new }
  7 +
  8 + before do
  9 + gitolite.stub(config: gitolite_config)
  10 + end
  11 +
  12 + it { should respond_to :set_key }
  13 + it { should respond_to :remove_key }
  14 +
  15 + it { should respond_to :update_repository }
  16 + it { should respond_to :create_repository }
  17 + it { should respond_to :remove_repository }
  18 +
  19 + it { gitolite.url_to_repo('diaspora').should == Gitlab.config.ssh_path + "diaspora.git" }
  20 +
  21 + it "should call config update" do
  22 + gitolite_config.should_receive(:update_project!)
  23 + gitolite.update_repository project
  24 + end
  25 +end
... ...
spec/mailers/notify_spec.rb
... ... @@ -24,7 +24,7 @@ describe Notify do
24 24 end
25 25  
26 26 it 'has the correct subject' do
27   - should have_subject /^gitlab \| Account was created for you$/
  27 + should have_subject /^gitlab \| Account was created for you$/i
28 28 end
29 29  
30 30 it 'contains the new user\'s login name' do
... ...
spec/models/note_spec.rb
... ... @@ -35,6 +35,16 @@ describe Note do
35 35 note = Factory(:note, note: "-1 for this")
36 36 note.should_not be_upvote
37 37 end
  38 +
  39 + it "recognizes a +1 emoji as a vote" do
  40 + note = build(:note, note: ":+1: for this")
  41 + note.should be_upvote
  42 + end
  43 +
  44 + it "recognizes a neutral emoji note" do
  45 + note = build(:note, note: "I would :+1: this, but I don't want to")
  46 + note.should_not be_upvote
  47 + end
38 48 end
39 49  
40 50 let(:project) { create(:project) }
... ...
spec/models/users_project_spec.rb
... ... @@ -10,7 +10,7 @@ describe UsersProject do
10 10 let!(:users_project) { create(:users_project) }
11 11  
12 12 it { should validate_presence_of(:user_id) }
13   - it { should validate_uniqueness_of(:user_id).scoped_to(:project_id) }
  13 + it { should validate_uniqueness_of(:user_id).scoped_to(:project_id).with_message(/already exists/) }
14 14  
15 15 it { should validate_presence_of(:project_id) }
16 16 end
... ...
spec/requests/api/projects_spec.rb
... ... @@ -30,38 +30,40 @@ describe Gitlab::API do
30 30  
31 31 describe "POST /projects" do
32 32 it "should create new project without code and path" do
33   - lambda {
34   - name = "foo"
35   - post api("/projects", user), {
36   - name: name
37   - }
38   - response.status.should == 201
39   - json_response["name"].should == name
40   - json_response["code"].should == name
41   - json_response["path"].should == name
42   - }.should change{Project.count}.by(1)
43   - end
44   - it "should create new project" do
45   - lambda {
46   - name = "foo"
47   - path = "bar"
48   - code = "bazz"
49   - post api("/projects", user), {
50   - code: code,
51   - path: path,
52   - name: name
53   - }
54   - response.status.should == 201
55   - json_response["name"].should == name
56   - json_response["path"].should == path
57   - json_response["code"].should == code
58   - }.should change{Project.count}.by(1)
59   - end
60   - it "should not create project without name" do
61   - lambda {
62   - post api("/projects", user)
63   - response.status.should == 404
64   - }.should_not change{Project.count}
  33 + expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1)
  34 + end
  35 +
  36 + it "should not create new project without name" do
  37 + expect { post api("/projects", user) }.to_not change {Project.count}
  38 + end
  39 +
  40 + it "should respond with 201 on success" do
  41 + post api("/projects", user), name: 'foo'
  42 + response.status.should == 201
  43 + end
  44 +
  45 + it "should repsond with 404 on failure" do
  46 + post api("/projects", user)
  47 + response.status.should == 404
  48 + end
  49 +
  50 + it "should assign attributes to project" do
  51 + project = Factory.attributes(:project, {
  52 + path: 'path',
  53 + code: 'code',
  54 + description: Faker::Lorem.sentence,
  55 + default_branch: 'stable',
  56 + issues_enabled: false,
  57 + wall_enabled: false,
  58 + merge_requests_enabled: false,
  59 + wiki_enabled: false
  60 + })
  61 +
  62 + post api("/projects", user), project
  63 +
  64 + project.each_pair do |k,v|
  65 + json_response[k.to_s].should == v
  66 + end
65 67 end
66 68 end
67 69  
... ...
spec/support/gitolite_stub.rb
... ... @@ -17,7 +17,7 @@ module GitoliteStub
17 17 )
18 18  
19 19 gitolite_admin = double(
20   - 'Gitolite::GitoliteAdmin',
  20 + 'Gitolite::GitoliteAdmin',
21 21 config: gitolite_config,
22 22 save: true,
23 23 )
... ... @@ -27,9 +27,21 @@ module GitoliteStub
27 27 end
28 28  
29 29 def stub_gitlab_gitolite
30   - gitlab_gitolite = Gitlab::Gitolite.new
31   - Gitlab::Gitolite.stub(new: gitlab_gitolite)
32   - gitlab_gitolite.stub(configure: ->() { yield(self) })
33   - gitlab_gitolite.stub(update_keys: true)
  30 + gitolite_config = double('Gitlab::GitoliteConfig')
  31 + gitolite_config.stub(
  32 + apply: ->() { yield(self) },
  33 + write_key: true,
  34 + rm_key: true,
  35 + update_projects: true,
  36 + update_project: true,
  37 + update_project!: true,
  38 + destroy_project: true,
  39 + destroy_project!: true,
  40 + admin_all_repo: true,
  41 + admin_all_repo!: true,
  42 +
  43 + )
  44 +
  45 + Gitlab::GitoliteConfig.stub(new: gitolite_config)
34 46 end
35 47 end
... ...