Private
Public Access
1
0

Compare commits

..

12 Commits

66 changed files with 3540 additions and 6702 deletions

View File

@@ -1,3 +1,3 @@
{ {
"presets": ["es2015", "react"] "presets": ["es2015", "es2016", "es2017", "react"]
} }

4
.gitignore vendored
View File

@@ -35,7 +35,5 @@ phpunit-report/*
/src/Mine/SeekerBundle/Resources/public/js/src/ /src/Mine/SeekerBundle/Resources/public/js/src/
nohup.out nohup.out
src/Mine/SeekerBundle/Resources/public/js/index.js src/Mine/SeekerBundle/Resources/public/js/build/
src/Mine/SeekerBundle/Resources/public/js/index.min.js
/src/Mine/SeekerBundle/Resources/public/js/index.js.map
npm-debug.log npm-debug.log

View File

@@ -1,105 +0,0 @@
Changelog
=========
0.4.0 (2019-10-26)
------------------
- Change session driver to REDIS. [Lang]
- Add created, updated field to db && improve graph design. [Lang]
- Cache setup && optimalize for google pagespeed && optimalize all
images. [Lang]
- Improve graph design on homepage && add footer and techs && add
official pages. [Lang]
- Bugfix mine websocket periodic mysql calling. [Lang]
- Bugfix hwioauth remember me && centralize hwioauth and facebook
settings. [Lang]
- Centralize jquery && bugfix mysql auto-termination problem w/ user
auth. [Lang]
- Release beta4. [Lang]
- Gitignore npm debug log. [Lang]
- Add english lang everywhere && add snowfall && add centralized version
nbr && improve stylesheet && slack integration. [Lang]
- Bugfix #30 && random bg in game. [Lang]
- Add google analytics and facebook scripts && improve url share method
w/ fb && enforce https in prod. [Lang]
- Reg and login buttons on index && remove list method && facebook
centralize. [Lang]
- Redesign user frontend. [Lang]
- Mods for performance; one js.min file on prod. [Lang]
- Improve webpack config for prod compile #23. [Lang]
- Ssl handling #22 && reconnection issues #20, #21. [Lang]
- Facebook prod settings w/ app; hwi/HWIOAuthBundle. [Lang]
- Refact && game reconnection and restore w/o refresh #3 && bugfix bomb
explosion on opponent mines #19. [Lang]
- Typo in rpc. [Lang]
- Handle prod mysql timeout && graphics improve. [Lang]
- Gitignore webpacked index.js. [Lang]
- Add production mods. [Lang]
- Bugfix points saving and exploded bombs to db && you can resign #6.
[Lang]
- Bugfix resign button existence #11. [Lang]
- Bugfix opponent bomb btn buzz on hover #10. [Lang]
- Bugfix points problem in the end #16. [Lang]
- Add desc to every user #9. [Lang]
- Clipboard - not working #8. [Lang]
- Random player on start #5. [Lang]
- Show left mines after end #2 && reduce network traffic && better
active field checking method. [Lang]
- Some refactor #13. [Lang]
- Bugfix grid field render #12. [Lang]
- Game ends after x mines. [Lang]
- Add new sounds && refactor && new bg images && form redesigns. [Lang]
- Bugfix entities gridrow, grid && improve graph design on homepage.
[Lang]
- Some refactor && prod settings. [Lang]
- Improve graphics design in game. [Lang]
- Bugfix grid row in entity. [Lang]
- Bugfix changePlayer after bomb explosion. [Lang]
- Improve game graph design. [Lang]
- Login and register form more design. [Lang]
- Add basic design to userbundle && refactor. [Lang]
- Add font-awesome. [Lang]
- Working user authentication w/ fb and plain login. [Lang]
- Add facebook login module, hwi/HWIOAuthBundle. [Lang]
- Login && register form overrided. [Lang]
- Js and config refactor. [Lang]
- Replace gridcol object to json array in db. [Lang]
- Refactor. [Lang]
- Save steps and point info to db. [Lang]
- Save the step data to db. [Lang]
- Renamed the acme to mineseeker && handle when the user connection has
been lost. [Lang]
- Add player names to UI. [Lang]
- Add overlay && game do not start until the opponent came. [Lang]
- Add base64 encryption to grid when it has been sended to server.
[Lang]
- On click opponents bomb, you cannot target && refactor. [Lang]
- Warning when player has been found more than 20 mines. [Lang]
- Bugfix center mine counter animation. [Lang]
- The opponent is the next when bomb is exploded. [Lang]
- Current username checked && refactor && remove players in channel when
they are more than 2. [Lang]
- Send bomb info and use it on opponent. [Lang]
- Add sounds w/ howler. [Lang]
- Bugfix multiple empty fields w/ one click on opponent view. [Lang]
- Refact && remove sound and logging && bugfix BIGBUG - handleGridField
and showAppropriateFields sort order... [Lang]
- Create first working communication. [Lang]
- Create entities and repositories. [Lang]
- Changed websocket default port && debug RPC. [Lang]
- Created working session and client handler w/ websocket. [Lang]
- Working websocket client and server w/o session handling and storage.
[Lang]
- Composer update. [Lang]
- Improve game && start sound creating. [Lang]
- Refactor grid control and grid field. [Lang]
- Created basic game w/ table and animations. [Lang]
- Websocket basic setup FE & BE && working basic game w/ react &&
webpack & babel config. [Lang]
- Gitignore node_modules && add symlink to node_modules (just for
install) && basic react. [Lang]
- Add react hello world. [Lang]
- Rename project in config. [Lang]
- Initial commit && create project in symfony3. [Lang]

View File

@@ -1,10 +1,3 @@
mineseeker
=========
A Symfony project created on September 22, 2016, 13:56 pm.
PROJECT VERSION 1.1.0-20191026
This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket. This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
0.) Must installed modules w/ npm are in package.json + to global: 0.) Must installed modules w/ npm are in package.json + to global:
@@ -22,7 +15,7 @@ This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
1.) Backend WebSocket server start as daemon - GeniusesOfSymfony/WebSocketBundle 1.) Backend WebSocket server start as daemon - GeniusesOfSymfony/WebSocketBundle
$ nohup bin/console gos:websocket:server & $ nohup php bin/console gos:websocket:server --env=prod &
2.) React JS WebPack watch generator w/ babel presets: es2015, react 2.) React JS WebPack watch generator w/ babel presets: es2015, react
@@ -39,4 +32,9 @@ This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
3.) Connect to Prod 3.) Connect to Prod
ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang $ ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang
4.) Stunnel config
$ sudo nano /etc/stunnel/stunnel.conf

View File

@@ -24,7 +24,7 @@ class AppKernel extends Kernel
new Gos\Bundle\PubSubRouterBundle\GosPubSubRouterBundle(), new Gos\Bundle\PubSubRouterBundle\GosPubSubRouterBundle(),
new Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(), new Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(),
new Snc\RedisBundle\SncRedisBundle(), new Snc\RedisBundle\SncRedisBundle(),
new CL\Bundle\SlackBundle\CLSlackBundle(), // new CL\Bundle\SlackBundle\CLSlackBundle(),
new Jotunheimr\AdminBundle\JotunheimrAdminBundle(), new Jotunheimr\AdminBundle\JotunheimrAdminBundle(),
new Jotunheimr\UserBundle\JotunheimrUserBundle(), new Jotunheimr\UserBundle\JotunheimrUserBundle(),

View File

@@ -10,8 +10,8 @@ imports:
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration # http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters: parameters:
locale: en locale: en
mailer_user: 7system7@gmail.com fos.email: 7system7@gmail.com
mailer_name: Lang fos.name: system7
framework: framework:
#esi: ~ #esi: ~
@@ -29,11 +29,11 @@ framework:
default_locale: "%locale%" default_locale: "%locale%"
trusted_hosts: ~ trusted_hosts: ~
trusted_proxies: ~ trusted_proxies: ~
session: ~ session:
# http://symfony.com/doc/current/reference/configuration/framework.html#handler-id # http://symfony.com/doc/current/reference/configuration/framework.html#handler-id
# handler_id: session.handler.native_file # handler_id: session.handler.native_file
# save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%" # save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
# handler_id: session.handler.pdo handler_id: session.handler.pdo
fragments: ~ fragments: ~
http_method_override: true http_method_override: true
assets: ~ assets: ~
@@ -43,7 +43,7 @@ twig:
debug: "%kernel.debug%" debug: "%kernel.debug%"
strict_variables: "%kernel.debug%" strict_variables: "%kernel.debug%"
globals: globals:
version: "0.37.18 (beta7)" version: "0.42.21 (beta8)"
facebook_api: "%facebook.api%" facebook_api: "%facebook.api%"
facebook_scope: "%facebook.scope%" facebook_scope: "%facebook.scope%"
facebook_api_version: "%facebook.version%" facebook_api_version: "%facebook.version%"
@@ -90,8 +90,8 @@ fos_user:
firewall_name: secured_area firewall_name: secured_area
user_class: Jotunheimr\UserBundle\Entity\User user_class: Jotunheimr\UserBundle\Entity\User
from_email: from_email:
address: "%mailer_user%" address: "%fos.email%"
sender_name: "%mailer_name%" sender_name: "%fos.name%"
# Facebook OAuth # Facebook OAuth
hwi_oauth: hwi_oauth:
@@ -108,5 +108,5 @@ hwi_oauth:
csrf: true csrf: true
# Slack integration # Slack integration
cl_slack: #cl_slack:
api_token: xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001 # api_token: xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001

View File

@@ -4,7 +4,7 @@ parameters:
database_port: null database_port: null
database_name: mine database_name: mine
database_user: root database_user: root
database_password: ~ database_password: bazmeg
mailer_transport: smtp mailer_transport: smtp
mailer_host: 127.0.0.1 mailer_host: 127.0.0.1
mailer_user: null mailer_user: null

View File

@@ -2,9 +2,12 @@
parameters: parameters:
database_host: 127.0.0.1 database_host: 127.0.0.1
database_port: null database_port: null
# database_name: xxsvci_mineseeker
# database_user: xxsvci_mine
# database_password: "XTw#8qC$faa*"
database_name: mine database_name: mine
database_user: root database_user: root
database_password: 'bazmeg' database_password: "bazmeg"
mailer_transport: smtp mailer_transport: smtp
mailer_host: 127.0.0.1 mailer_host: 127.0.0.1
mailer_user: null mailer_user: null

View File

@@ -17,9 +17,9 @@
} }
}, },
"require": { "require": {
"php": ">=7.3", "php": ">=5.5.9",
"symfony/symfony": "3.*", "symfony/symfony": "3.1.*",
"doctrine/orm": "^2.6", "doctrine/orm": "^2.5",
"doctrine/doctrine-bundle": "^1.6", "doctrine/doctrine-bundle": "^1.6",
"doctrine/doctrine-cache-bundle": "^1.3", "doctrine/doctrine-cache-bundle": "^1.3",
"symfony/swiftmailer-bundle": "^2.3", "symfony/swiftmailer-bundle": "^2.3",
@@ -36,10 +36,9 @@
"snc/redis-bundle": "^2.0", "snc/redis-bundle": "^2.0",
"hwi/oauth-bundle": "^0.5.1", "hwi/oauth-bundle": "^0.5.1",
"cleentfaar/slack-bundle": "^0.20.1", "cleentfaar/slack-bundle": "^0.20.1",
"ext-json": "*" "symfony/translation": "^3.2"
}, },
"require-dev": { "require-dev": {
"roave/security-advisories": "dev-master",
"sensio/generator-bundle": "^3.0", "sensio/generator-bundle": "^3.0",
"symfony/phpunit-bridge": "^3.0" "symfony/phpunit-bridge": "^3.0"
}, },

3474
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
src/Mine/SeekerBundle/Resources/public/js/node

3543
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@
"babel-core": "^6.14.0", "babel-core": "^6.14.0",
"babel-loader": "^6.2.5", "babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.14.0", "babel-preset-es2015": "^6.14.0",
"babel-preset-es2016": "^6.22.0",
"babel-preset-es2017": "^6.22.0",
"babel-preset-react": "^6.11.1", "babel-preset-react": "^6.11.1",
"howler": "^2.0.1", "howler": "^2.0.1",
"js-base64": "^2.1.9", "js-base64": "^2.1.9",
@@ -16,7 +18,7 @@
"react-dom": "^15.3.2", "react-dom": "^15.3.2",
"uglify-js": "^2.7.4", "uglify-js": "^2.7.4",
"uglifycss": "0.0.25", "uglifycss": "0.0.25",
"webpack": "^1.15.0" "webpack": "^1.13.3"
}, },
"devDependencies": {}, "devDependencies": {},
"scripts": { "scripts": {
@@ -33,6 +35,6 @@
"multiplayer", "multiplayer",
"websocket" "websocket"
], ],
"author": "Laszlo Lang <system7>", "author": "Laszlo Lang <system7> www.laszlolang.com",
"license": "ISC" "license": "ISC"
} }

View File

@@ -26,6 +26,9 @@ class User extends BaseUser
*/ */
private $facebookId; private $facebookId;
/**
* @ORM\Column(name="facebook_access_token", type="string", length=255, nullable=true)
*/
private $facebookAccessToken; private $facebookAccessToken;
/** /**

View File

@@ -251,6 +251,11 @@ main div.txt {
margin: 50px auto 0 auto; margin: 50px auto 0 auto;
} }
main .txt h1 {
font-weight: bold;
text-align: center;
}
main div.txt h2 { main div.txt h2 {
margin: 0 0 50px 0; margin: 0 0 50px 0;
} }
@@ -276,10 +281,6 @@ main .technologies img {
margin: 20px; margin: 20px;
} }
main .technologies h1 {
font-weight: bold;
}
footer { footer {
background: #414040; background: #414040;
width: 100%; width: 100%;
@@ -320,7 +321,7 @@ footer nav ul li a:hover {
color: #FFFFFF; color: #FFFFFF;
} }
@media screen and (max-width: 1100px) { @media screen and (max-width: 1200px) {
header section #id_welcome { header section #id_welcome {
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@@ -2,7 +2,7 @@
{% trans_default_domain 'FOSUserBundle' %} {% trans_default_domain 'FOSUserBundle' %}
{% block title %} - Sign up{% endblock %} {% block title %} - Registration{% endblock %}
{% block fos_user_content %} {% block fos_user_content %}
<section class="header-content"> <section class="header-content">
@@ -12,7 +12,7 @@
border="0"/> border="0"/>
</a> </a>
</div> </div>
<h1> Sign up </h1> <h1> Registration </h1>
<div class="db"> <div class="db">
<form action="{{ path('fos_user_registration_register') }}" <form action="{{ path('fos_user_registration_register') }}"
method="post"> method="post">

View File

@@ -2,7 +2,7 @@
{% trans_default_domain 'FOSUserBundle' %} {% trans_default_domain 'FOSUserBundle' %}
{% block title %} - Sign in{% endblock %} {% block title %} - Login{% endblock %}
{% block fos_user_content %} {% block fos_user_content %}
{% if error %} {% if error %}
@@ -16,7 +16,7 @@
border="0"/> border="0"/>
</a> </a>
</div> </div>
<h1>Sign in</h1> <h1>Login</h1>
<form action="{{ path("fos_user_security_check") }}" method="post" <form action="{{ path("fos_user_security_check") }}" method="post"
class="ac-custom ac-checkbox ac-boxfill"> class="ac-custom ac-checkbox ac-boxfill">
@@ -32,7 +32,7 @@
<input type="text" id="username" name="_username" value="{{ last_username }}" <input type="text" id="username" name="_username" value="{{ last_username }}"
class="form-input form-username" class="form-input form-username"
placeholder="Username or Email"/> placeholder="Username or Email" autofocus />
<input type="password" id="password" name="_password" <input type="password" id="password" name="_password"
class="form-input" class="form-input"

View File

@@ -35,6 +35,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
type="text/javascript"></script> type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8}); $(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});

View File

@@ -72,6 +72,7 @@ class MyFOSUBUserProvider extends BaseFOSUBProvider
} }
$user->setFacebookId($response->getUsername()); $user->setFacebookId($response->getUsername());
$user->setFacebookAccessToken($response->getAccessToken());
$user->setEmail($response->getEmail()); $user->setEmail($response->getEmail());
$user->setEmailCanonical($response->getEmail()); $user->setEmailCanonical($response->getEmail());
$user->setUsername($this->slug($response->getRealName())); $user->setUsername($this->slug($response->getRealName()));

View File

@@ -5,21 +5,64 @@ namespace Mine\SeekerBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
/**
* Class GameController
*
* @package Mine\SeekerBundle\Controller
* @author system7 <https://www.laszlolang.com>
*/
class GameController extends Controller class GameController extends Controller
{ {
public function indexAction() public function indexAction(Request $request)
{ {
return $this->render('MineSeekerBundle:Game:index.html.twig'); // $apiClient = new ApiClient('xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001');
//
// $usersListPayload = new UsersListPayload();
// $usersList = $apiClient->send($usersListPayload);
//
// dump($usersList);
//
// $payload = new ChatPostMessagePayload();
// $payload->setChannel('#general');
// $payload->setAsUser(true);
// $payload->setText('Hello world!');
// $response = $apiClient->send($payload);
// if ($response->isOk()) {
// dump('bazmeg');
// } else {
// dump($response->getError());
// dump($response->getErrorExplanation());
// }
return $this->render('MineSeekerBundle:Game:index.html.twig', array(
'env' => $this->container->getParameter('kernel.environment'),
'ssl' => $request->isSecure() ? 'true' : 'false',
));
} }
public function playAction(Request $request) public function playAction(Request $request)
{ {
return $this->render('MineSeekerBundle:Game:play.html.twig', array( return $this->render('MineSeekerBundle:Game:play.html.twig', array(
'env' => $this->container->getParameter('kernel.environment'), 'env' => $this->container->getParameter('kernel.environment'),
'ssl' => $request->isSecure() ? 'true' : 'false' 'ssl' => $request->isSecure() ? 'true' : 'false',
)); ));
} }
public function rePlayAction(Request $request)
{
return $this->render('MineSeekerBundle:Game:play.html.twig', array(
'env' => $this->container->getParameter('kernel.environment'),
'ssl' => $request->isSecure() ? 'true' : 'false',
));
}
public function slackAction(Request $request)
{
}
public function privacyAction() public function privacyAction()
{ {
return $this->render('MineSeekerBundle:Official:privacy.html.twig'); return $this->render('MineSeekerBundle:Official:privacy.html.twig');

View File

@@ -5,7 +5,10 @@ namespace Mine\SeekerBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* Gamer * Class Gamer
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* *
* @ORM\Table(name="gamer") * @ORM\Table(name="gamer")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GamerRepository") * @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GamerRepository")

View File

@@ -7,7 +7,10 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* Grid * Class Grid
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* *
* @ORM\Table(name="grid") * @ORM\Table(name="grid")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRepository") * @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRepository")

View File

@@ -5,7 +5,10 @@ namespace Mine\SeekerBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* GridRow * Class GridRow
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* *
* @ORM\Table(name="grid_row") * @ORM\Table(name="grid_row")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRowRepository") * @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRowRepository")

View File

@@ -8,7 +8,10 @@ use Doctrine\ORM\Mapping as ORM;
use Jotunheimr\UserBundle\Entity\User; use Jotunheimr\UserBundle\Entity\User;
/** /**
* PlayedGame * Class PlayedGame
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* *
* @ORM\Table(name="played_game") * @ORM\Table(name="played_game")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\PlayedGameRepository") * @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\PlayedGameRepository")

View File

@@ -443,4 +443,52 @@ class PlayedGame
{ {
return $this->resign; return $this->resign;
} }
/**
* Set created
*
* @param \DateTime $created
*
* @return PlayedGame
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* @param \DateTime $updated
*
* @return PlayedGame
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
} }

View File

@@ -5,7 +5,10 @@ namespace Mine\SeekerBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* Step * Class Step
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* *
* @ORM\Table(name="step") * @ORM\Table(name="step")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\StepRepository") * @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\StepRepository")

View File

@@ -162,4 +162,28 @@ class Step
{ {
return $this->wBomb; return $this->wBomb;
} }
/**
* Set created
*
* @param \DateTime $created
*
* @return Step
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
} }

View File

@@ -7,6 +7,12 @@ use Gos\Bundle\WebSocketBundle\Event\ClientErrorEvent;
use Gos\Bundle\WebSocketBundle\Event\ServerEvent; use Gos\Bundle\WebSocketBundle\Event\ServerEvent;
use Gos\Bundle\WebSocketBundle\Event\ClientRejectedEvent; use Gos\Bundle\WebSocketBundle\Event\ClientRejectedEvent;
/**
* Class MineseekerClientEventListener
*
* @package Mine\SeekerBundle\EventListener
* @author system7 <https://www.laszlolang.com>
*/
class MineseekerClientEventListener class MineseekerClientEventListener
{ {
/** /**

View File

@@ -5,6 +5,12 @@ namespace Mine\SeekerBundle\Periodic;
use Gos\Bundle\WebSocketBundle\Periodic\PdoPeriodicPing; use Gos\Bundle\WebSocketBundle\Periodic\PdoPeriodicPing;
use Gos\Bundle\WebSocketBundle\Periodic\PeriodicInterface; use Gos\Bundle\WebSocketBundle\Periodic\PeriodicInterface;
/**
* Class MinePeriodic
*
* @package Mine\SeekerBundle\Periodic
* @author system7 <https://www.laszlolang.com>
*/
class MinePeriodic implements PeriodicInterface class MinePeriodic implements PeriodicInterface
{ {
/** @var PdoPeriodicPing */ /** @var PdoPeriodicPing */
@@ -18,7 +24,8 @@ class MinePeriodic implements PeriodicInterface
/** /**
* This function is executed every 5 seconds. * This function is executed every 5 seconds.
* *
* For more advanced functionality, try injecting a Topic Service to perform actions on your connections every x seconds. * For more advanced functionality, try injecting a Topic Service to perform actions on your connections every x
* seconds.
*/ */
public function tick() public function tick()
{ {

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository; namespace Mine\SeekerBundle\Repository;
/** /**
* GamerRepository * Class GamerRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * @package Mine\SeekerBundle\Repository
* repository methods below. * @author system7 <https://www.laszlolang.com>
*/ */
class GamerRepository extends \Doctrine\ORM\EntityRepository class GamerRepository extends \Doctrine\ORM\EntityRepository
{ {

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository; namespace Mine\SeekerBundle\Repository;
/** /**
* GridColRepository * Class GridColRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * @package Mine\SeekerBundle\Repository
* repository methods below. * @author system7 <https://www.laszlolang.com>
*/ */
class GridColRepository extends \Doctrine\ORM\EntityRepository class GridColRepository extends \Doctrine\ORM\EntityRepository
{ {

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository; namespace Mine\SeekerBundle\Repository;
/** /**
* GridRepository * Class GridRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * @package Mine\SeekerBundle\Repository
* repository methods below. * @author system7 <https://www.laszlolang.com>
*/ */
class GridRepository extends \Doctrine\ORM\EntityRepository class GridRepository extends \Doctrine\ORM\EntityRepository
{ {

View File

@@ -4,10 +4,10 @@ namespace Mine\SeekerBundle\Repository;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
/** /**
* GridRowRepository * Class GridRowRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * @package Mine\SeekerBundle\Repository
* repository methods below. * @author system7 <https://www.laszlolang.com>
*/ */
class GridRowRepository extends EntityRepository class GridRowRepository extends EntityRepository
{ {

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository; namespace Mine\SeekerBundle\Repository;
/** /**
* PlayedGameRepository * Class PlayedGameRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * @package Mine\SeekerBundle\Repository
* repository methods below. * @author system7 <https://www.laszlolang.com>
*/ */
class PlayedGameRepository extends \Doctrine\ORM\EntityRepository class PlayedGameRepository extends \Doctrine\ORM\EntityRepository
{ {

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository; namespace Mine\SeekerBundle\Repository;
/** /**
* StepRepository * Class StepRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * @package Mine\SeekerBundle\Repository
* repository methods below. * @author system7 <https://www.laszlolang.com>
*/ */
class StepRepository extends \Doctrine\ORM\EntityRepository class StepRepository extends \Doctrine\ORM\EntityRepository
{ {

View File

@@ -10,18 +10,18 @@ doctrine_cache:
type: redis type: redis
alias: gos_web_socket.client_storage.driver.redis alias: gos_web_socket.client_storage.driver.redis
## SNC # SNC
#snc_redis: snc_redis:
# clients: clients:
# cache: cache:
# type: predis type: predis
# alias: cache alias: cache
# dsn: redis://localhost/2 dsn: redis://localhost/2
# logging: "%kernel.debug%" logging: "%kernel.debug%"
# options: options:
# profile: 2.2 profile: 2.2
# connection_timeout: 10 connection_timeout: 10
# read_write_timeout: 30 read_write_timeout: 30
# Web Socket Configuration # Web Socket Configuration
gos_web_socket: gos_web_socket:
@@ -33,10 +33,10 @@ gos_web_socket:
- "@MineSeekerBundle/Resources/config/pubsub/routing.yml" - "@MineSeekerBundle/Resources/config/pubsub/routing.yml"
client: client:
firewall: secured_area firewall: secured_area
# session_handler: "@session.handler.pdo" session_handler: "@session.handler.pdo"
# storage: storage:
# driver: "@gos_web_socket.client_storage.driver.predis" driver: "@gos_web_socket.client_storage.driver.predis"
# ttl: 28800 #(optionally) time to live if you use redis driver ttl: 28800 #(optionally) time to live if you use redis driver
# prefix: client #(optionally) prefix if you use redis driver, create key "client:1" instead key "1" prefix: client #(optionally) prefix if you use redis driver, create key "client:1" instead key "1"
periodic: periodic:
- "@mineseeker.periodic" - "@mineseeker.periodic"

View File

@@ -1,4 +1,4 @@
# Topic Configuration # MineSeeker Topic Configuration
mineseeker_topic: mineseeker_topic:
channel: mineseeker/channel/{game} channel: mineseeker/channel/{game}
handler: handler:
@@ -7,6 +7,12 @@ mineseeker_topic:
# method: # method:
# path: '[a-z1-9A-Z]+' # path: '[a-z1-9A-Z]+'
# UserList Topic Configuration
userList_topic:
channel: mineseeker/userList
handler:
callback: 'userlist.topic'
# Remote Procedure Call Configuration # Remote Procedure Call Configuration
mineseeker_rpc: mineseeker_rpc:
channel: mineseeker-rpc/{method} channel: mineseeker-rpc/{method}

View File

@@ -13,6 +13,11 @@ MineSeekerBundle_gamePlayWId:
defaults: { _controller: MineSeekerBundle:Game:play } defaults: { _controller: MineSeekerBundle:Game:play }
schemes: [https] schemes: [https]
MineSeekerBundle_gameReplay:
path: /re-play/{gameAssoc}
defaults: { _controller: MineSeekerBundle:Game:rePlay }
schemes: [https]
MineSeekerBundle_slack: MineSeekerBundle_slack:
path: /slack path: /slack
defaults: { _controller: MineSeekerBundle:Game:slack } defaults: { _controller: MineSeekerBundle:Game:slack }

View File

@@ -1,33 +1,55 @@
services: services:
pdo:
class: PDO
arguments:
dsn: "mysql:host=%database_host%;dbname=%database_name%"
user: "%database_user%"
passwd: "%database_password%"
calls:
- [ setAttribute, [3, 2] ] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION
session.handler.pdo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
public: false
arguments: ["@pdo", {lock_mode: 0}]
# config.yml --> gos_web_socket > storage > client > driver # config.yml --> gos_web_socket > storage > client > driver
# gos_web_socket.client_storage.driver.predis: gos_web_socket.client_storage.driver.predis:
# class: Gos\Bundle\WebSocketBundle\Client\Driver\PredisDriver class: Gos\Bundle\WebSocketBundle\Client\Driver\PredisDriver
# arguments: arguments:
# - "@snc_redis.cache" - "@snc_redis.cache"
mineseeker.periodic: mineseeker.periodic:
class: Mine\SeekerBundle\Periodic\MinePeriodic class: Mine\SeekerBundle\Periodic\MinePeriodic
tags: tags:
- { name: gos_web_socket.periodic } - { name: gos_web_socket.periodic }
arguments: arguments:
- '@gos_web_socket.pdo.periodic_ping' ping: '@gos_web_socket.pdo.periodic_ping'
mineseeker.topic_sample_service: mineseeker.game_service:
class: Mine\SeekerBundle\Topic\MineseekerTopic class: Mine\SeekerBundle\Topic\MineseekerTopic
tags: tags:
- { name: gos_web_socket.topic } - { name: gos_web_socket.topic }
arguments: arguments:
- "@gos_web_socket.websocket.client_manipulator" clientManipulator: "@gos_web_socket.websocket.client_manipulator"
- '@doctrine.orm.entity_manager' doctrine: '@doctrine.orm.entity_manager'
- '@request_stack' requestStack: '@request_stack'
mineseeker.rpc_sample_service: mineseeker.user_list_service:
class: Mine\SeekerBundle\Topic\UserListTopic
tags:
- { name: gos_web_socket.topic }
arguments:
clientManipulator: "@gos_web_socket.websocket.client_manipulator"
doctrine: '@doctrine.orm.entity_manager'
requestStack: '@request_stack'
mineseeker.game_rpc_service:
class: Mine\SeekerBundle\Rpc\MineseekerRpc class: Mine\SeekerBundle\Rpc\MineseekerRpc
tags: tags:
- { name: gos_web_socket.rpc } - { name: gos_web_socket.rpc }
arguments: arguments:
- '@doctrine.orm.entity_manager' doctrine: '@doctrine.orm.entity_manager'
- '@logger'
gos_web_socket_server.client_event.listener: gos_web_socket_server.client_event.listener:
class: Mine\SeekerBundle\EventListener\MineseekerClientEventListener class: Mine\SeekerBundle\EventListener\MineseekerClientEventListener

View File

@@ -141,7 +141,162 @@ header section div.buttons > a.small:hover {
transition: all 250ms ease-in-out; transition: all 250ms ease-in-out;
} }
@media screen and (max-width: 1100px) { main .user-list-container {
display: flex;
align-items: flex-start;
justify-content: center;
flex-wrap: wrap;
width: 100%;
}
main .user-list-container .user-friend {
background: #ccc;
width: 15%;
min-width: 145px;
text-align: center;
padding: 10px;
margin: 5px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
main .user-list-container .user-friend h1 {
display: block;
font-size: 14px;
text-align: center;
}
main .user-list-container .user-friend .img,
main .user-list-container .user-friend .img img {
width: 100%;
}
main .user-list-container .user-friend button {
background: #83aed9;
display: inline-block;
font: bold 22px 'Rajdhani', sans-serif;
border: 1px solid #6890ba;
color: #FFFFFF;
padding: 10px;
-webkit-transition: all 250ms ease-in-out;
-moz-transition: all 250ms ease-in-out;
-o-transition: all 250ms ease-in-out;
transition: all 250ms ease-in-out;
-webkit-border-radius: 3px;
border-radius: 3px;
}
main .user-list-container .user-friend button:hover {
background: #86b5e1;
border: 1px solid #658fb8;
color: #FFFFFF;
-webkit-transition: all 250ms ease-in-out;
-moz-transition: all 250ms ease-in-out;
-o-transition: all 250ms ease-in-out;
transition: all 250ms ease-in-out;
}
main .user-list-container .user-friend button.button-offline {
background: #a1a1a1;
border: 1px solid #929292;
cursor: default;
}
main .user-list-container .user-friend button.button-offline:hover {
background: #a1a1a1;
border: 1px solid #929292;
}
main .user-list-filter form {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
}
main .user-list-filter input {
border: 1px solid #ccc;
border-right: 0;
min-width: 300px;
padding: 10px;
-webkit-border-top-left-radius: 3px;
border-top-left-radius: 3px;
-webkit-border-bottom-left-radius: 3px;
border-bottom-left-radius: 3px;
}
main .user-list-filter button {
background: #ccc;
width: 42px;
height: 42px;
border: 0;
color: #fff;
-webkit-border-top-right-radius: 3px;
border-top-right-radius: 3px;
-webkit-border-bottom-right-radius: 3px;
border-bottom-right-radius: 3px;
}
main .user-request-container {
display: flex;
align-items: center;
justify-content: center;
}
main .user-request-container .user-friend {
background: #ff0000;
width: 150px;
color: #403f3f;
padding: 10px;
margin: 10px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
main .user-request-container h1 {
font-size: 14px;
color: #fff;
}
main .user-request-container .img,
main .user-request-container .img img {
width: 100%;
}
main .user-request-container button {
background: #fff;
width: 100%;
font-weight: bold;
color: #ff0000;
border: 0;
padding: 15px 5px;
margin: 0 auto;
-webkit-border-radius: 3px;
border-radius: 3px;
-webkit-transition: all 250ms ease-in-out;
-moz-transition: all 250ms ease-in-out;
-o-transition: all 250ms ease-in-out;
transition: all 250ms ease-in-out;
}
main .user-request-container button:hover {
background: #ffc38b;
-webkit-transition: all 250ms ease-in-out;
-moz-transition: all 250ms ease-in-out;
-o-transition: all 250ms ease-in-out;
transition: all 250ms ease-in-out;
}
@media screen and (max-width: 1200px) {
header section { header section {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -166,11 +321,6 @@ header section div.buttons > a.small:hover {
} }
@media screen and (max-width: 500px) { @media screen and (max-width: 500px) {
/*header {*/
/*min-height: 100%;*/
/*height: auto;*/
/*}*/
header section { header section {
width: auto; width: auto;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -2,9 +2,12 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import MineSeeker from './mine-seeker/app'; import MineSeeker from './mine-seeker/app';
let mineWrapper = document.getElementById('mine-wrapper');
ReactDOM.render( ReactDOM.render(
<MineSeeker env={document.getElementById('mine-wrapper').dataset.env} <MineSeeker env={mineWrapper.dataset.env}
gameId={document.getElementById('mine-wrapper').dataset.gameId} gameId={mineWrapper.dataset.gameId}
ssl={document.getElementById('mine-wrapper').dataset.ssl}/>, webPlayerName={mineWrapper.dataset.webPlayerName}
document.getElementById('mine-wrapper') ssl={mineWrapper.dataset.ssl}/>,
mineWrapper
); );

View File

@@ -1,49 +1,39 @@
import React from 'react'; import React from 'react';
import Grid from './grid/grid';
import GridControl from './grid/grid-control'; import GridControl from './grid/grid-control';
import MineServices from '../mine-system/mine-services';
import MineStart from './game/start'
import MineEnd from './game/end'
import MineWebsocket from './game/websocket'
class MineSeeker extends React.Component { class MineSeeker extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(50); let services = new MineServices();
let gameAssoc = props.gameId !== '' ? props.gameId : services.randomString(50);
let channel = "mineseeker/channel/" + gameAssoc; let channel = "mineseeker/channel/" + gameAssoc;
this.state = { this.state = {
startProcess: new MineStart(),
endProcess: new MineEnd(),
wsProcess: new MineWebsocket(),
services: services,
env: props.env, env: props.env,
ssl: props.ssl, ssl: props.ssl,
gameInherited: props.gameId !== '', gameInherited: props.gameId !== '',
webPlayerName: props.webPlayerName === '' ? null : props.webPlayerName,
gameAssoc: gameAssoc, gameAssoc: gameAssoc,
channel: channel, channel: channel,
session: null, session: null,
createGrid: false, createGrid: false,
stepCache: [], stepCache: [],
connectionLost: false, connectionLost: false,
end: false end: false,
replay: false
} }
} }
makeGameAssoc(len) {
let text = "";
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < len; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
currectGridSize() {
let $field = $('#mine-wrapper .grid');
$field.height($field.width());
$field = $('#mine-wrapper .grid .field-wrapper');
$field.height($field.width());
$('#mine-wrapper .grid .field-wrapper .field')
.height($field.width())
.css('line-height', ($field.width() - 2) + 'px');
}
/** /**
* STEP * STEP
* *
@@ -71,338 +61,21 @@ class MineSeeker extends React.Component {
} }
/** /**
* START * Unregistered
* http://mine.dev/re-play/I1Bx9UHZP5CWDnTZHpJqGTlkzehblfsbfz4A4xYaH9HFhBK2aN
* *
* @param payload * Registered - Admin -> Lang
* http://mine.dev/re-play/bazmegdflgkjndfgkhjn
*/ */
makeGameStart(payload) {
/** every time the blue starts */
this.refs.gridControl.refs.userControl.setState({activePlayer: 1});
/** Set up player names w/ server data */
this.refs.gridControl.refs.userControl.refs.red.setState({
name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon,
});
this.refs.gridControl.refs.userControl.refs.blue.setState({
name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon,
desc: this.refs.gridControl.state.webPlayer === 'blue'
? this.refs.gridControl.state.desc.you
: this.refs.gridControl.state.desc.buddy,
active: true,
});
this.refs.gridControl.setState({overlay: false});
}
/**
* THE END
*
* @param bluePoints
* @param redPoints
* @param resign
*/
makeGameEndIfItEnds(bluePoints, redPoints, resign = false) {
let redWins = redPoints > 25,
blueWins = bluePoints > 25;
if (redWins || blueWins || resign) {
this.refs.gridControl.state.sound.won.play();
if (false === resign) {
this.refs.gridControl.setState({
overlay: true,
overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!",
overlaySubTitle: "Play again!"
});
}
this.refs.gridControl.showLeftMines();
this.refs.gridControl.refs.userControl.setState({activePlayer: false});
this.refs.gridControl.refs.userControl.refs.red.setState({desc: ""});
this.refs.gridControl.refs.userControl.refs.blue.setState({desc: ""});
}
}
resignProcess(color) {
this.refs.gridControl.setState({
overlay: true,
overlayTitle: color === this.refs.gridControl.state.webPlayer
? "You have been give up"
: "Your opponent has been resigned",
overlaySubTitle: color === this.refs.gridControl.state.webPlayer
? "You LOSE!"
: "You WIN!"
});
this.setState({end: true});
this.makeGameEndIfItEnds(0, 0, true);
}
clickResign() {
/** PUBLISH */
this.state.session.publish(this.state.channel, {
'resign': this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red'
});
this.resignProcess(this.refs.gridControl.state.webPlayer);
}
clickResignCancel() {
this.refs.gridControl.setState({
overlay: false
});
}
/** RESIGN */
resign() {
let users = this.refs.gridControl.refs.userControl,
activePlayer = users.state.activePlayer ? 'blue' : 'red';
if (this.refs.gridControl.state.webPlayer === activePlayer) {
this.refs.gridControl.setState({
overlay: true,
overlayTitle: "Are u sure u want to resign?!",
overlaySubTitle: <div className="resign">
<a onClick={this.clickResign.bind(this)}>Yes</a>
<a onClick={this.clickResignCancel.bind(this)}>No!</a>
</div>
});
}
}
/**
* @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus
* @see https://developers.facebook.com/docs/sharing/reference/send-dialog
* @see https://developers.facebook.com/docs/plugins/share-button/
*/
clickFBShare() {
let display = 'popup';
FB.getLoginStatus(function (response) {
display = response.status === 'connected'
? 'dialog'
: 'popup';
});
FB.ui({
method: 'send',
display: display,
link: window.location.href + '/' + this.state.gameAssoc,
});
}
wInit(session, gridServer, gridClient) {
this.setState({session: session});
/** save session to GridControl */
/** render grid fields - @see #12 */
this.refs.gridControl.setState({
grid: this.state.gameInherited ? gridServer : gridClient,
channel: this.state.channel,
desc: {
buddy: <div>
Your buddy is <br/>
making a <br/>
move.
</div>,
you: <div>
It is your turn! <br/>
Make a move.
</div>
},
overlay: true,
overlayTitle: "We are waiting for your opponent...",
overlaySubTitle: this.state.gameAssoc
?
<div>
<h3>Share this unique link w/ your opponent</h3>
<div className="clippy">
<input id="foo"
defaultValue={window.location.href + '/' + this.state.gameAssoc}/>
</div>
{this.state.env !== 'dev' &&
<a onClick={this.clickFBShare.bind(this)}>Share in Facebook message</a>
}
{this.state.env === 'dev' &&
<a href={"/play/" + this.state.gameAssoc} target="_blank">Play w/ me!</a>
}
</div>
: '',
renderGridFields: this.state.gameAssoc
});
}
wSubscribe(payload, rpcUsers = null) {
this.state.env === 'dev' && console.info(
(typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!"
);
let firstUser = !rpcUsers;
this.refs.gridControl.state.webPlayer === null && this.refs.gridControl.setState({
webPlayer: payload.user === payload.users.blue ||
(
firstUser && payload.users.blueAnon !== '' ||
!firstUser && (rpcUsers.blueAnon === '' && rpcUsers.blue === '')
)
? 'blue' : 'red'
});
/** rwd */
(900 > $(document).width()) && this.currectGridSize();
/** every user has been came */
if (
payload.userCnt === 2 &&
(
!this.state.connectionLost ||
this.state.connectionLost && false === this.refs.gridControl.refs.userControl.state.activePlayer && !this.state.end
)
) {
this.makeGameStart(payload);
}
}
wUnsubscribe(payload) {
this.state.env === 'dev' && console.info(payload.msg);
this.refs.gridControl.setState({
overlay: true,
overlayTitle: "The connection has been lost w/ your friend...",
overlaySubTitle: "Please, restart the game!"
});
}
wTopic(payload) {
/** Auto-Step if this player is not the current user */
if (this.refs.gridControl.state.webPlayer !== payload.data.player) {
if (null === payload.data.resign) {
this.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]);
this.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process');
this.refs.gridControl.refs.userControl.setState({bombSelected: payload.data.bomb});
/** STEP */
let points = this.makePointsCalcAndStep(payload.data.coords);
/** THE END */
this.makeGameEndIfItEnds(points.blue, points.red);
} else {
/** RESIGN */
/** THE END */
this.resignProcess(payload.data.resign);
}
}
}
/** Connect - Subscribe */
subscribe(rpcUsers = null) {
this.state.session.subscribe(
this.state.channel,
(uri, payload, log) => {
let isTopicEvent = typeof payload.data !== 'undefined',
isNotUnsubscribe = typeof payload.msg === 'undefined';
/** CONNECTION */
if (isTopicEvent) {
this.wTopic(payload);
} else {
if (isNotUnsubscribe) {
this.wSubscribe(payload, rpcUsers);
} else {
this.wUnsubscribe(payload);
}
}
/** RECONNECTION */
if (payload.userCnt === 2 && this.state.connectionLost) {
this.state.env === 'dev' && console.info('Reconnection process');
/** PUBLISH */
let cache = this.state.stepCache;
cache.forEach((item) => this.state.session.publish(this.state.channel, item));
this.setState({connectionLost: false, stepCache: []});
}
});
}
connectWithWebsocket() {
/** Create Websocket w/ Bahnhof.js */
let websocket = WS.connect(
this.state.env === 'dev'
? "ws://localhost:6450"
// : (this.state.ssl === 'true' ? "wss" : "ws") + "://" + window.location.hostname + ":6450/"
: (this.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450/"
);
/**
* Connect
* Session is an Autobahn JS WAMP session.
*/
websocket.on("socket/connect", (session) => {
this.state.env === 'dev' && console.info("Successfully connected to the Server!");
if (!this.state.connectionLost) {
let gridClient = this.state.gameInherited || new Grid().state.grid;
/**
* Connect - RPC
* Send grid information to the server
*/
session
.call(
this.state.gameInherited ? "mineseeker-rpc/connectGame" : "mineseeker-rpc/startGame",
this.state.gameInherited ? this.state.gameAssoc : [Base64.encode(JSON.stringify(gridClient)), this.state.gameAssoc]
)
.then(
(data) => {
this.state.env === 'dev' && console.info('RPC has been called');
let serverData = data[0] !== true
? JSON.parse(Base64.decode(data))
: data;
/** Check the grid if the user is inherited @see #30 */
if ((this.state.gameInherited && null !== serverData.grid) || !this.state.gameInherited) {
this.wInit(session, serverData.grid, gridClient);
this.subscribe(this.state.gameInherited && serverData.users);
} else {
this.refs.gridControl.setState({
overlay: true,
overlayTitle: "This channel does not exists!",
overlaySubTitle: <a href={"/play"} target="_self">Restart game!</a>
});
console.error("This channel does not exists!");
}
},
(error, desc) => this.state.env === 'dev' && console.error(["RPC Error", error, desc])
);
} else {
this.setState({session: session});
this.subscribe();
}
});
/**
* DisConnect
* Error provides us with some insight into the disconnection: error.reason and error.code
*/
websocket.on("socket/disconnect", (error) => {
this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
error.code === 6 && this.setState({connectionLost: true});
error.code === 3 && setTimeout(function () {
this.componentDidMount();
}.bind(this), 500);
});
}
/** After rendering */ /** After rendering */
componentDidMount() { componentDidMount() {
this.connectWithWebsocket(); /** is it a REPLAY */
window.location.pathname.indexOf('re-play') > 0
? this.setState({replay: true})
: this.setState({replay: false});
this.state.wsProcess.connectWithWebsocket(this);
} }
/** /**
@@ -427,7 +100,7 @@ class MineSeeker extends React.Component {
let points = this.makePointsCalcAndStep(coords); let points = this.makePointsCalcAndStep(coords);
/** THE END */ /** THE END */
this.makeGameEndIfItEnds(points.blue, points.red); this.state.endProcess.makeGameEndIfItEnds(this, points.blue, points.red);
let dataPack = { let dataPack = {
'coords': coords, 'coords': coords,
@@ -452,7 +125,7 @@ class MineSeeker extends React.Component {
return ( return (
<GridControl ref="gridControl" <GridControl ref="gridControl"
env={this.props.env === 'dev'} env={this.props.env === 'dev'}
resign={this.resign.bind(this)} resign={this.state.endProcess.resign.bind(this.state.endProcess, this)}
onClick={this.onClick.bind(this)}/> onClick={this.onClick.bind(this)}/>
); );
} }

View File

@@ -0,0 +1,87 @@
import React from 'react';
class MineEnd extends React.Component {
constructor() {
super();
}
makeGameEndIfItEnds(app, bluePoints, redPoints, resign = false) {
let redWins = redPoints > 25,
blueWins = bluePoints > 25,
gridControl = app.refs.gridControl,
userControl = app.refs.gridControl.refs.userControl,
bluePlayer = userControl.refs.blue,
redPlayer = userControl.refs.red;
if (redWins || blueWins || resign) {
gridControl.state.sound.won.play();
/** it is NOT a RESIGN */
if (false === resign) {
gridControl.setState({
overlay: true,
overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!",
overlaySubTitle: "Play again!"
});
}
gridControl.showLeftMines();
userControl.setState({activePlayer: false});
redPlayer.setState({desc: ""});
bluePlayer.setState({desc: ""});
}
}
resignProcess(app, webPlayer) {
let gridControl = app.refs.gridControl;
gridControl.setState({
overlay: true,
overlayTitle: webPlayer === gridControl.state.webPlayer
? "You have been give up"
: "Your opponent has been resigned",
overlaySubTitle: webPlayer === gridControl.state.webPlayer
? "You LOSE!"
: "You WIN!"
});
app.setState({end: true});
this.makeGameEndIfItEnds(app, 0, 0, true);
}
clickResign(app) {
let gridControl = app.refs.gridControl,
userControl = app.refs.gridControl.refs.userControl;
/** REMOTE */
app.state.session.publish(app.state.channel, {
'resign': userControl.state.activePlayer ? 'blue' : 'red'
});
/** LOCAL */
this.resignProcess(app, gridControl.state.webPlayer);
}
clickResignCancel(gridControl) {
gridControl.setState({overlay: false});
}
resign(app) {
let gridControl = app.refs.gridControl,
userControl = app.refs.gridControl.refs.userControl,
activePlayer = userControl.state.activePlayer ? 'blue' : 'red';
if (gridControl.state.webPlayer === activePlayer) {
gridControl.setState({
overlay: true,
overlayTitle: "Are u sure u want to resign?!",
overlaySubTitle: <div className="resign">
<a onClick={this.clickResign.bind(this, app)}>Yes</a>
<a onClick={this.clickResignCancel.bind(this, gridControl)}>No!</a>
</div>
});
}
}
}
export default MineEnd;

View File

@@ -0,0 +1,66 @@
import React from 'react';
class MineStart extends React.Component {
constructor() {
super();
}
/** MAIN */
makeGameStart(app, payload, gridControl) {
let steps = JSON.parse(Base64.decode(payload.steps)),
userControl = gridControl.refs.userControl;
if (steps.length) {
this.takeSteps(app, steps, gridControl);
} else {
/** every time the blue starts when it is not a continued game */
userControl.setState({activePlayer: 1});
}
this.setupPlayers(payload, gridControl, userControl);
}
/**
* Take steps if them exists
*
* @param app
* @param steps
* @param userControl
*/
takeSteps(app, steps, userControl) {
steps.forEach((item) => {
setTimeout(() => {
userControl.setState({bombSelected: item.wBomb});
app.makePointsCalcAndStep([item.row, item.col]);
}, 500);
});
}
/**
* Set up player names w/ server data
*
* @param payload
* @param gridControl
* @param userControl
*/
setupPlayers(payload, gridControl, userControl) {
let redPlayer = userControl.refs.red,
bluePlayer = userControl.refs.blue;
redPlayer.setState({
name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon,
});
bluePlayer.setState({
name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon,
desc: gridControl.state.webPlayer === 'blue'
? gridControl.state.desc.you
: gridControl.state.desc.buddy,
active: true,
});
gridControl.setState({overlay: false});
}
}
export default MineStart;

View File

@@ -0,0 +1,10 @@
import React from 'react';
class MineStep extends React.Component {
constructor() {
super();
}
}
export default MineStep;

View File

@@ -0,0 +1,266 @@
import React from 'react';
import Grid from '../grid/grid';
class MineWebsocket extends React.Component {
constructor() {
super();
}
handleUnsubscribe(app, payload) {
let gridControl = app.refs.gridControl;
app.state.env === 'dev' && console.info(payload.msg);
gridControl.setState({
overlay: true,
overlayTitle: "The connection has been lost w/ your friend...",
overlaySubTitle: "Please, restart the game!"
});
}
nbrUsersWasInGame(users) {
let nbr = 0;
nbr += users.blueAnon === "" ? 0 : 1;
nbr += users.redAnon === "" ? 0 : 1;
nbr += users.blue === "" ? 0 : 1;
nbr += users.red === "" ? 0 : 1;
return nbr;
}
wereYouInTheParty(users, user) {
return users.blueAnon === user ||
users.redAnon === user ||
users.blue === user ||
users.red === user;
}
whichWereYouInTheParty(users, user) {
return users.blue === user ? 'blue' : 'red';
}
handleSubscribe(app, payload) {
let gridControl = app.refs.gridControl,
user = payload.user,
usersInTheParty = payload.users,
webPlayerName = app.state.webPlayerName;
app.state.env === 'dev' && console.info(
(typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!"
);
/** set up webPlayer */
if (gridControl.state.webPlayer === null) {
if (app.state.replay) {
if (this.wereYouInTheParty(usersInTheParty, webPlayerName)) {
let opponent = user === webPlayerName && webPlayerName === payload.users.red
? payload.users.blue
: payload.users.red;
gridControl.setState({
webPlayer: this.whichWereYouInTheParty(usersInTheParty, webPlayerName),
overlayTitle: "We are waitin' for " + opponent + "!",
overlaySubTitle: ""
});
} else {
gridControl.setState({
overlayTitle: "It was not your party!",
overlaySubTitle: ""
});
}
} else {
gridControl.setState({
webPlayer: payload.user === payload.users.blue || payload.user === payload.users.blueAnon
? 'blue' : 'red',
overlaySubTitle: app.state.gameAssoc
?
<div>
<h3>Share this unique link w/ your opponent</h3>
<div className="clippy">
<input id="foo"
defaultValue={window.location.href + '/' + app.state.gameAssoc}/>
</div>
{app.state.env !== 'dev' &&
<a onClick={this.clickFBShare.bind(this, app)}>Share in Facebook message</a>
}
{app.state.env === 'dev' &&
<a href={"/play/" + app.state.gameAssoc} target="_blank">Play w/ me!</a>
}
</div>
: ''
});
}
}
if (payload.userCnt === 2 && !app.state.end) {
app.state.startProcess.makeGameStart(app, payload, gridControl);
}
/** rwd */
(900 > $(document).width()) && app.state.services.currectGridSize();
}
handleTopic(app, payload) {
let gridControl = app.refs.gridControl,
userControl = gridControl.refs.userControl;
/** Auto-Step if this player is not the current user */
if (gridControl.state.webPlayer !== payload.data.player) {
if (null === payload.data.resign) {
app.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]);
app.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process');
userControl.setState({bombSelected: payload.data.bomb});
/** STEP */
let points = app.makePointsCalcAndStep(payload.data.coords);
/** THE END? */
app.state.endProcess.makeGameEndIfItEnds(app, points.blue, points.red);
} else {
/** RESIGN */
/** THE END */
app.state.endProcess.resignProcess(app, payload.data.resign);
}
}
}
/** Connect - Subscribe */
subscribe(app) {
app.state.session.subscribe(
app.state.channel,
(uri, payload, log) => {
let isTopicEvent = typeof payload.data !== 'undefined',
isNotUnsubscribe = typeof payload.msg === 'undefined';
/** CONNECTION */
if (isTopicEvent) {
this.handleTopic(app, payload);
} else {
if (isNotUnsubscribe) {
this.handleSubscribe(app, payload);
} else {
this.handleUnsubscribe(app, payload);
}
}
/** RECONNECTION */
if (payload.userCnt === 2 && app.state.connectionLost) {
app.state.env === 'dev' && console.info('Reconnection process');
let cache = app.state.stepCache;
cache.forEach((item) => app.state.session.publish(app.state.channel, item));
app.setState({connectionLost: false, stepCache: []});
}
});
}
connectWithWebsocket(app) {
/** Create Websocket w/ Bahnhof.js */
let websocket = WS.connect(
app.state.env === 'dev'
? "ws://mine.dev:6450"
: (app.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450/"
);
/**
* Connect
* Session is an Autobahn JS WAMP session.
*/
websocket.on("socket/connect", (session) => {
app.state.env === 'dev' && console.info("Successfully connected to the Server!");
if (!app.state.connectionLost) {
let gridClient = new Grid().state.grid;
/**
* Connect - RPC
* Send grid information to the server
*/
session
.call(
"mineseeker-rpc/connectGame",
[Base64.encode(JSON.stringify(gridClient)), app.state.gameAssoc]
)
.then(
(data) => {
app.state.env === 'dev' && console.info('RPC has been called');
let grid = JSON.parse(Base64.decode(data));
this.init(app, session, grid);
this.subscribe(app);
},
(error, desc) => app.state.env === 'dev' && console.error(["RPC Error", error, desc])
);
} else {
app.setState({session: session});
this.subscribe(app);
}
});
/**
* DisConnect
* Error provides us with some insight into the disconnection: error.reason and error.code
*/
websocket.on("socket/disconnect", (error) => {
app.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
error.code === 6 && app.setState({connectionLost: true});
error.code === 3 && setTimeout(function () {
app.componentDidMount();
}.bind(this), 500);
});
}
init(app, session, grid) {
let gridControl = app.refs.gridControl;
app.setState({session: session});
/** save session to GridControl */
/** render grid fields - @see #12 */
gridControl.setState({
grid: grid,
channel: app.state.channel,
desc: {
buddy: <div>
Your buddy is <br/>
making a <br/>
move.
</div>,
you: <div>
It is your turn! <br/>
Make a move.
</div>
},
overlay: true,
overlayTitle: "We are waiting for your opponent...",
overlaySubTitle: "",
renderGridFields: app.state.gameAssoc
});
}
/**
* @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus
* @see https://developers.facebook.com/docs/sharing/reference/send-dialog
* @see https://developers.facebook.com/docs/plugins/share-button/
*/
clickFBShare(app) {
let display = 'popup';
FB.getLoginStatus(function (response) {
display = response.status === 'connected'
? 'dialog'
: 'popup';
});
FB.ui({
method: 'send',
display: display,
link: window.location.href + '/' + app.state.gameAssoc,
});
}
}
export default MineWebsocket;

View File

@@ -0,0 +1,33 @@
import React from 'react';
class MineServices extends React.Component {
constructor() {
super();
}
randomString(len) {
let text = "";
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < len; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
/**
* RWD
*/
currectGridSize() {
let $field = $('#mine-wrapper .grid');
$field.height($field.width());
$field = $('#mine-wrapper .grid .field-wrapper');
$field.height($field.width());
$('#mine-wrapper .grid .field-wrapper .field')
.height($field.width())
.css('line-height', ($field.width() - 2) + 'px');
}
}
export default MineServices;

View File

@@ -0,0 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import MineUserList from './mine-user-list/app';
let mineUserList = document.getElementById('mine-user-list');
ReactDOM.render(
<MineUserList env={mineUserList.dataset.env}
ssl={mineUserList.dataset.ssl}
webPlayerName={mineUserList.dataset.webPlayerName}/>,
mineUserList
);

View File

@@ -0,0 +1,194 @@
import React from 'react';
import MineServices from '../mine-system/mine-services';
class MineUserList extends React.Component {
constructor(props) {
super();
let services = new MineServices();
this.state = {
env: props.env,
ssl: props.ssl,
webPlayerName: props.webPlayerName,
services: services,
session: null,
users: [],
requests: new Map(),
search: '',
};
}
refreshUserList(payload) {
let webUsers = JSON.parse(Base64.decode(payload.users)),
users = new Map();
webUsers.forEach((item) => {
if (!users.has(item.email) && item.username !== this.state.webPlayerName) {
users.set(item.email, item);
}
});
this.setState({users: users});
}
connectWithWebsocket() {
let websocket = WS.connect(
this.state.env === 'dev'
? "ws://mine.dev:6450"
: (this.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450/"
);
websocket.on("socket/connect", (session) => {
this.state.env === 'dev' && console.info("Successfully connected to the Server!");
this.state.session = session;
session.subscribe(
'mineseeker/userList',
(uri, payload, log) => {
/** refresh list */
if (typeof payload.users !== 'undefined') {
this.refreshUserList(payload);
}
/** communication */
if (typeof payload.gameAssoc !== 'undefined') {
switch (payload.type) {
case 'REQ':
let req = this.state.requests,
user = JSON.parse(Base64.decode(payload.user));
if (!req.has(user.username)) {
req.set(user.username, {
'gameAssoc': payload.gameAssoc,
'user': user,
});
}
this.setState({reqests: req});
break;
case 'RESP':
window.location.href = window.location.origin + '/re-play/' + payload.gameAssoc;
break;
case 'SEARCH':
break;
}
}
});
});
websocket.on("socket/disconnect", (error) => {
this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
});
}
componentDidMount() {
this.connectWithWebsocket();
}
getProfilePicture(id) {
let avatarNbr = 5;
if (id !== null) {
avatarNbr = id.substr(-1) > 5 ? 5 : id.substr(-1);
}
return id !== null ?
<img src={"http://graph.facebook.com/" + id + "/picture?type=square&width=130&height=130"}
alt="Facebook profile"/> :
<img src={"/bundles/mineseeker/images/avatar/bg-avatar-default-" + avatarNbr + ".png"}
alt="Default avatar picture"/>;
}
clickChallengeFriend(isOnline, username) {
if (isOnline) {
this.state.session.publish('mineseeker/userList', {
'type': 'REQ',
'username': username,
'gameAssoc': this.state.services.randomString(50)
});
}
return 0;
}
clickChallengeAccepted(username, gameAssoc) {
this.state.session.publish('mineseeker/userList', {
'type': 'RESP',
'username': username,
'gameAssoc': gameAssoc
});
window.location.href = window.location.origin + '/re-play/' + gameAssoc;
}
onChangeSearch(event) {
this.setState({search: event.target.value});
}
handleSubmit(event) {
this.state.session.publish('mineseeker/userList', {
'type': 'SEARCH',
'username': this.state.search
});
event.preventDefault();
}
render() {
let users = [];
let req = [];
this.state.users.size > 0
? this.state.users.forEach((item) => {
users.push(<div key={this.state.services.randomString(50)} className="user-friend">
<div className="img">
{this.getProfilePicture(item.id)}
</div>
<h1>{item.name}</h1>
<button className={item.online ? 'button-online' : 'button-offline'}
onClick={this.clickChallengeFriend.bind(this, item.online, item.username)}>
{item.online ? 'Challenge' : 'Offline'}
</button>
</div>)
})
: '';
this.state.requests.size > 0
? this.state.requests.forEach((item) => {
req.push(<div key={this.state.services.randomString(50)} className="user-friend">
<div className="img">
{this.getProfilePicture(item.user.id)}
</div>
<h1>{item.user.name} <br/><strong>challenged you</strong>!</h1>
<button onClick={this.clickChallengeAccepted.bind(this, item.user.username, item.gameAssoc)}>
Accept
</button>
</div>)
})
: '';
return (
<div>
<div className="user-request-container">
{req}
</div>
<div className="user-list-filter">
<form onSubmit={this.handleSubmit.bind(this)}>
<input type="text"
onChange={this.onChangeSearch.bind(this)}
value={this.state.search}
autoFocus="autoFocus"
placeholder="Search users..."/>
<button type="submit"><i className="fa fa-search"></i></button>
</form>
</div>
<div className="user-list-container">
{users.length ? users : 'There is no users w/ this search results..'}
</div>
</div>
);
}
}
export default MineUserList;

View File

@@ -48,8 +48,8 @@
{{ 'layout.logout'|trans({}, 'FOSUserBundle') }} {{ 'layout.logout'|trans({}, 'FOSUserBundle') }}
</a> </a>
{% else %} {% else %}
<a href="{{ path('fos_user_registration_register') }}" class="small">Sign up</a> &middot; <a href="{{ path('fos_user_registration_register') }}" class="small">Registration</a> &middot;
<a href="{{ path('fos_user_security_login') }}" class="small">Sign in</a> <a href="{{ path('fos_user_security_login') }}" class="small">Login</a>
{% endif %} {% endif %}
</div> </div>
<h3> <h3>
@@ -63,6 +63,14 @@
{% block body %} {% block body %}
<div class="txt"> <div class="txt">
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
<h1>Users</h1>
<div id="mine-user-list"
data-env="{{ env }}"
data-ssl="{{ ssl }}"
data-web-player-name="{{ app.user.username is defined ? app.user.username : '' }}">
</div>
{% endif %}
<div class="technologies"> <div class="technologies">
<h1>Used technologies</h1> <h1>Used technologies</h1>
<img src="{{ asset('bundles/mineseeker/images/technologies/websocket.png') }}" alt="Used Websocket" <img src="{{ asset('bundles/mineseeker/images/technologies/websocket.png') }}" alt="Used Websocket"
@@ -93,6 +101,7 @@
{% block stylesheets %} {% block stylesheets %}
{{ parent() }} {{ parent() }}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="https://fonts.googleapis.com/css?family=Rajdhani:300,400,500,600,700&amp;subset=latin-ext" <link href="https://fonts.googleapis.com/css?family=Rajdhani:300,400,500,600,700&amp;subset=latin-ext"
rel="stylesheet"> rel="stylesheet">
@@ -107,11 +116,31 @@
{% block javascripts %} {% block javascripts %}
{{ parent() }} {{ parent() }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js" {% if is_granted("IS_AUTHENTICATED_REMEMBERED") and app.user.facebookAccessToken is defined %}
type="text/javascript"></script> {% if env == 'dev' %}
<script type="text/javascript"> {% javascripts filter='?uglifyjs2'
$(function () { '@GosWebSocketBundle/Resources/public/js/vendor/autobahn.min.js'
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8}); '@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
}); '@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
</script> '@MineSeekerBundle/Resources/public/js/build/mine-user-list/index.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
{% else %}
{% javascripts filter='?uglifyjs2'
'@GosWebSocketBundle/Resources/public/js/vendor/autobahn.min.js'
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
'@MineSeekerBundle/Resources/public/js/build/mine-user-list/index.min.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});
});
</script>
{% endif %}
{% endif %}
{% endblock %} {% endblock %}

View File

@@ -10,7 +10,8 @@
<div id="mine-wrapper" <div id="mine-wrapper"
data-env="{{ env }}" data-env="{{ env }}"
data-ssl="{{ ssl }}" data-ssl="{{ ssl }}"
data-game-id="{{ app.request.get('gameAssoc') }}"> data-game-id="{{ app.request.get('gameAssoc') }}"
data-web-player-name="{{ app.user.username is defined ? app.user.username : '' }}">
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
@@ -20,7 +21,8 @@
<meta property="og:type" content="website"/> <meta property="og:type" content="website"/>
<meta property="og:title" content="Your friend challenges YOU!"/> <meta property="og:title" content="Your friend challenges YOU!"/>
<meta property="og:description" content="Do you accept the challenge?"/> <meta property="og:description" content="Do you accept the challenge?"/>
<meta property="og:image" content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('bundles/mineseeker/images/mine-1600x627.png') }}"/> <meta property="og:image"
content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('bundles/mineseeker/images/mine-1600x627.png') }}"/>
{% endblock %} {% endblock %}
{% block stylesheets %} {% block stylesheets %}
@@ -46,7 +48,7 @@
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js' '@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
'@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js' '@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js'
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js' '@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
'@MineSeekerBundle/Resources/public/js/index.js' %} '@MineSeekerBundle/Resources/public/js/build/mine-seeker/index.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script> <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %} {% endjavascripts %}
{% else %} {% else %}
@@ -55,7 +57,7 @@
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js' '@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
'@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js' '@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js'
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js' '@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
'@MineSeekerBundle/Resources/public/js/index.min.js' %} '@MineSeekerBundle/Resources/public/js/build/mine-seeker/index.min.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script> <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %} {% endjavascripts %}
{% endif %} {% endif %}

View File

@@ -1,14 +1,21 @@
<script type="text/javascript"> <script type="text/javascript">
window.fbAsyncInit = function () { $(function () {
FB.init({ window.fbAsyncInit = function () {
appId: '{{ facebook_api }}', FB.init({
xfbml: true, appId: '{{ facebook_api }}',
cookie: true, xfbml: true,
status: true, cookie: true,
oauth: true, status: true,
version: '{{ facebook_api_version }}' version: '{{ facebook_api_version }}',
}); });
};
/** trigger jQuery when Facebook SKD loads */
// $(document).trigger('fb-load');
// $(document).bind('fb-load', function () {
// TODO
// });
};
});
(function (d, s, id) { (function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0]; var js, fjs = d.getElementsByTagName(s)[0];

View File

@@ -4,31 +4,35 @@ namespace Mine\SeekerBundle\Rpc;
use Doctrine\DBAL\Driver\PDOException; use Doctrine\DBAL\Driver\PDOException;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\OptimisticLockException;
use Mine\SeekerBundle\Entity\Grid; use Mine\SeekerBundle\Entity\Grid;
use Mine\SeekerBundle\Entity\GridRow; use Mine\SeekerBundle\Entity\GridRow;
use Mine\SeekerBundle\Entity\PlayedGame; use Mine\SeekerBundle\Entity\PlayedGame;
use Psr\Log\LoggerInterface;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use Gos\Bundle\WebSocketBundle\RPC\RpcInterface; use Gos\Bundle\WebSocketBundle\RPC\RpcInterface;
use Gos\Bundle\WebSocketBundle\Router\WampRequest; use Gos\Bundle\WebSocketBundle\Router\WampRequest;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class MineseekerRpc
*
* @package Mine\SeekerBundle\Rpc
* @author system7 <https://www.laszlolang.com>
*/
class MineseekerRpc implements RpcInterface class MineseekerRpc implements RpcInterface
{ {
/** @var EntityManager */ /** @var EntityManager */
protected $em; protected $em;
protected $grid;
private $logger;
/** /**
* MineseekerRpc constructor. * MineseekerRpc constructor.
*
* @param EntityManager $entityManager * @param EntityManager $entityManager
*/ */
public function __construct(EntityManager $entityManager, LoggerInterface $logger) public function __construct(EntityManager $entityManager)
{ {
$this->em = $entityManager; $this->em = $entityManager;
$this->logger = $logger;
} }
/** /**
@@ -43,94 +47,53 @@ class MineseekerRpc implements RpcInterface
/** /**
* @param ConnectionInterface $connection * @param ConnectionInterface $connection
* @param WampRequest $request * @param WampRequest $request
* @param array $params * @param array $params
* @return boolean *
*/
public function startGame(ConnectionInterface $connection, WampRequest $request, array $params)
{
$this->reConnect($params);
return $this->saveGrid($params);
}
/**
* @param ConnectionInterface $connection
* @param WampRequest $request
* @param array $params
* @return string Json string for frontend w/ numbering consideration. (=> a number is not string) * @return string Json string for frontend w/ numbering consideration. (=> a number is not string)
* @throws OptimisticLockException
*/ */
public function connectGame(ConnectionInterface $connection, WampRequest $request, array $params) public function connectGame(ConnectionInterface $connection, WampRequest $request, array $params)
{ {
$grid = $this->getGrid($params); $grid = $this->getGrid($params);
$users = null !== $grid ? $this->getUsers($params) : null; return base64_encode(json_encode($grid));
return base64_encode(json_encode(
array(
'grid' => $grid,
'users' => $users
)
));
}
/**
* @param $gameAssoc
* @return array
*/
private function getGrid($gameAssoc)
{
$this->reConnect();
$getsee = array();
$this->em->clear();
$grid = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
if (null !== $grid) {
foreach ($grid->getGrid()->getGridRow()->toArray() as $row) {
$getsee[] = $row->getGridCol();
}
return $getsee;
}
return null;
}
/**
* @param $gameAssoc
* @return array
*/
private function getUsers($gameAssoc)
{
$this->reConnect();
return $this->getUserCollection(
$this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc)
);
}
/**
* Get user collection from PlayedGame entity
*
* @param $playedGame
* @return array
*/
private function getUserCollection($playedGame)
{
return array(
'red' => null !== $playedGame->getRed() ? $playedGame->getRed()->getUsername() : '',
'blue' => null !== $playedGame->getBlue() ? $playedGame->getBlue()->getUsername() : '',
'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '',
'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : ''
);
} }
/** /**
* @param $data * @param $data
*
* @return array|bool
* @throws OptimisticLockException
*/
private function getGrid($data)
{
$this->reConnect();
$this->em->clear();
$grid = array();
/** @var PlayedGame $game */
$game = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($data[1]);
if (null !== $game) {
/** @var GridRow $row */
foreach ($game->getGrid()->getGridRow()->toArray() as $row) {
$grid[] = $row->getGridCol();
}
return $grid;
} else {
$this->saveGrid($data);
return $this->getGrid($data);
}
}
/**
* @param $data
*
* @return boolean * @return boolean
* @throws OptimisticLockException
*/ */
private function saveGrid($data) private function saveGrid($data)
{ {
@@ -174,8 +137,8 @@ class MineseekerRpc implements RpcInterface
$connection->close(); $connection->close();
$connection->connect(); $connection->connect();
} }
} catch(PDOException $ex) { } catch (PDOException $ex) {
$this->logger->error($ex->getMessage()); throw new NotFoundHttpException();
} }
} }
} }

View File

@@ -4,15 +4,25 @@ namespace Mine\SeekerBundle\Topic;
use Doctrine\DBAL\Driver\PDOException; use Doctrine\DBAL\Driver\PDOException;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\OptimisticLockException;
use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface; use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface;
use Gos\Bundle\WebSocketBundle\Topic\TopicInterface; use Gos\Bundle\WebSocketBundle\Topic\TopicInterface;
use Gos\Bundle\WebSocketBundle\Router\WampRequest; use Gos\Bundle\WebSocketBundle\Router\WampRequest;
use HWI\Bundle\OAuthBundle\Tests\Fixtures\FOSUser;
use Mine\SeekerBundle\Entity\Gamer; use Mine\SeekerBundle\Entity\Gamer;
use Mine\SeekerBundle\Entity\PlayedGame;
use Mine\SeekerBundle\Entity\Step; use Mine\SeekerBundle\Entity\Step;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use Ratchet\Wamp\Topic; use Ratchet\Wamp\Topic;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class MineseekerTopic
*
* @package Mine\SeekerBundle\Topic
* @author system7 <https://www.laszlolang.com>
*/
class MineseekerTopic implements TopicInterface class MineseekerTopic implements TopicInterface
{ {
/** @var ClientManipulatorInterface */ /** @var ClientManipulatorInterface */
@@ -27,9 +37,9 @@ class MineseekerTopic implements TopicInterface
/** /**
* MineseekerTopic constructor. * MineseekerTopic constructor.
* *
* @param $clientManipulator ClientManipulatorInterface * @param $clientManipulator ClientManipulatorInterface
* @param EntityManager $entityManager * @param EntityManager $entityManager
* @param RequestStack $requestStack * @param RequestStack $requestStack
*/ */
public function __construct(ClientManipulatorInterface $clientManipulator, EntityManager $entityManager, RequestStack $requestStack) public function __construct(ClientManipulatorInterface $clientManipulator, EntityManager $entityManager, RequestStack $requestStack)
{ {
@@ -42,9 +52,11 @@ class MineseekerTopic implements TopicInterface
* This will receive any Subscription requests for this topic. * This will receive any Subscription requests for this topic.
* *
* @param ConnectionInterface $connection * @param ConnectionInterface $connection
* @param Topic $topic * @param Topic $topic
* @param WampRequest $request * @param WampRequest $request
*
* @return void * @return void
* @throws OptimisticLockException
*/ */
public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request) public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{ {
@@ -58,13 +70,14 @@ class MineseekerTopic implements TopicInterface
} else { } else {
$users = $this->controlUsers($topic, $userName, $user); $users = $this->controlUsers($topic, $userName, $user);
$topic->broadcast([ $topic->broadcast(array(
'userTopicId' => $connection->resourceId, 'userTopicId' => $connection->resourceId,
'channel' => $topic->getId(), 'channel' => $topic->getId(),
'user' => $userName, 'user' => $userName,
'userCnt' => $topic->count(), 'userCnt' => $topic->count(),
'users' => $users 'users' => $users,
]); 'steps' => $this->getSteps($topic),
));
} }
} }
@@ -72,8 +85,9 @@ class MineseekerTopic implements TopicInterface
* This will receive any UnSubscription requests for this topic. * This will receive any UnSubscription requests for this topic.
* *
* @param ConnectionInterface $connection * @param ConnectionInterface $connection
* @param Topic $topic * @param Topic $topic
* @param WampRequest $request * @param WampRequest $request
*
* @return void * @return void
*/ */
public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request) public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
@@ -86,14 +100,16 @@ class MineseekerTopic implements TopicInterface
* This will receive any Publish requests for this topic. * This will receive any Publish requests for this topic.
* *
* @param ConnectionInterface $connection * @param ConnectionInterface $connection
* @param Topic $topic * @param Topic $topic
* @param WampRequest $request * @param WampRequest $request
* @param $event * @param $event
* @param array $exclude * @param array $exclude
* @param array $eligible * @param array $eligible
*
* @return mixed|void * @return mixed|void
* @internal param Topic $Topic * @internal param Topic $Topic
* @internal param array $eligibles * @internal param array $eligibles
* @throws OptimisticLockException
*/ */
public function onPublish(ConnectionInterface $connection, Topic $topic, WampRequest $request, $event, array $exclude, array $eligible) public function onPublish(ConnectionInterface $connection, Topic $topic, WampRequest $request, $event, array $exclude, array $eligible)
{ {
@@ -131,12 +147,15 @@ class MineseekerTopic implements TopicInterface
* *
* @param $topic * @param $topic
* @param $color * @param $color
*
* @throws OptimisticLockException
*/ */
private function saveResignToDb($topic, $color) private function saveResignToDb($topic, $color)
{ {
$this->reConnect(); $this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2]; $gameAssoc = explode('/', $topic->getId())[2];
/** @var PlayedGame $playedGame */
$playedGame = $this->em $playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame') ->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc); ->findOneByGameAssoc($gameAssoc);
@@ -149,14 +168,17 @@ class MineseekerTopic implements TopicInterface
/** /**
* Save steps and point information to database * Save steps and point information to database
* *
* @param $topic * @param Topic $topic
* @param $event * @param $event
*
* @throws OptimisticLockException
*/ */
private function saveStepToDb($topic, $event) private function saveStepToDb($topic, $event)
{ {
$this->reConnect(); $this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2]; $gameAssoc = explode('/', $topic->getId())[2];
/** @var PlayedGame $playedGame */
$playedGame = $this->em $playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame') ->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc); ->findOneByGameAssoc($gameAssoc);
@@ -180,19 +202,44 @@ class MineseekerTopic implements TopicInterface
$this->em->flush(); $this->em->flush();
} }
private function getSteps($topic)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
$steps = array();
foreach ($playedGame->getStep()->toArray() as $item) {
$steps[] = array(
'row' => $item->getRow(),
'col' => $item->getCol(),
'wBomb' => $item->getWBomb(),
);
}
return base64_encode(json_encode($steps));
}
/** /**
* Control all users in a channel * Control all users in a channel
* *
* @param $topic * @param $topic
* @param $userName * @param $userName
* @param $user * @param $user
*
* @return array * @return array
* @throws OptimisticLockException
*/ */
private function controlUsers($topic, $userName, $user) private function controlUsers($topic, $userName, $user)
{ {
$this->reConnect(); $this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2]; $gameAssoc = explode('/', $topic->getId())[2];
/** @var PlayedGame $playedGame */
$playedGame = $this->em $playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame') ->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc); ->findOneByGameAssoc($gameAssoc);
@@ -208,7 +255,7 @@ class MineseekerTopic implements TopicInterface
/** This checks it is a reconnection */ /** This checks it is a reconnection */
if (($one && ($red + $blue === 0)) || ($two && ($red + $blue === 1))) { if (($one && ($red + $blue === 0)) || ($two && ($red + $blue === 1))) {
/** @var $users {array} w/ save users to database */ /** @var $users {array} w/ save users to database */
$users = $this->saveUserToDb($topic, $userName, $user, $topic->count()); $users = $this->saveUserToDb($playedGame, $userName, $user, $topic->count());
} }
return $users; return $users;
@@ -217,20 +264,17 @@ class MineseekerTopic implements TopicInterface
/** /**
* Save user data to database * Save user data to database
* *
* @param $topic * @param PlayedGame $playedGame
* @param $userName * @param FOSUser $userName
* @param $user * @param $user
* @param $count * @param $count
*
* @return array * @return array
* @throws OptimisticLockException
*/ */
private function saveUserToDb($topic, $userName, $user, $count) private function saveUserToDb($playedGame, $userName, $user, $count)
{ {
$this->reConnect(); $this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
/** the user is not anonym */ /** the user is not anonym */
if (!is_string($user)) { if (!is_string($user)) {
@@ -275,7 +319,8 @@ class MineseekerTopic implements TopicInterface
/** /**
* Get user collection from PlayedGame entity * Get user collection from PlayedGame entity
* *
* @param $playedGame * @param PlayedGame $playedGame
*
* @return array * @return array
*/ */
private function getUserCollection($playedGame) private function getUserCollection($playedGame)
@@ -300,8 +345,8 @@ class MineseekerTopic implements TopicInterface
$connection->close(); $connection->close();
$connection->connect(); $connection->connect();
} }
} catch(PDOException $ex) { } catch (PDOException $ex) {
throw PDOException::class; throw new NotFoundHttpException();
} }
} }
} }

View File

@@ -0,0 +1,283 @@
<?php
namespace Mine\SeekerBundle\Topic;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\ORM\EntityManager;
use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface;
use Gos\Bundle\WebSocketBundle\Topic\TopicInterface;
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\Topic;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class UserListTopic
*
* @package Mine\SeekerBundle\Topic
* @author system7 <https://www.laszlolang.com>
*/
class UserListTopic implements TopicInterface
{
/** @var ClientManipulatorInterface */
protected $clientManipulator;
/** @var EntityManager */
protected $em;
/** @var RequestStack */
protected $requestStack;
/**
* MineseekerTopic constructor.
*
* @param $clientManipulator ClientManipulatorInterface
* @param EntityManager $entityManager
* @param RequestStack $requestStack
*/
public function __construct(ClientManipulatorInterface $clientManipulator, EntityManager $entityManager, RequestStack $requestStack)
{
$this->clientManipulator = $clientManipulator;
$this->em = $entityManager;
$this->requestStack = $requestStack;
}
/**
* This will receive any Subscription requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
*
* @return void
*/
public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{
/** this will broadcast the message to ALL subscribers of this topic. */
$user = $this->clientManipulator->getClient($connection);
$userName = is_string($user) ? $user : $user->getUsername();
/** @var array Find all users online and offline $users */
$users = $this->findAllUsers($topic);
$topic->broadcast([
'userTopicId' => $connection->resourceId,
'channel' => $topic->getId(),
'user' => $userName,
'userCnt' => $topic->count(),
'users' => base64_encode(json_encode($users))
]);
}
/**
* This will receive any UnSubscription requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
*
* @return void
*/
public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{
/** this will broadcast the message to ALL subscribers of this topic. */
$topic->broadcast(['msg' => $connection->resourceId . " has left " . $topic->getId()]);
}
/**
* This will receive any Publish requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
* @param $event
* @param array $exclude
* @param array $eligible
*
* @return mixed|void
* @internal param Topic $Topic
* @internal param array $eligibles
*/
public function onPublish(ConnectionInterface $connection, Topic $topic, WampRequest $request, $event, array $exclude, array $eligible)
{
$user = $this->clientManipulator->getClient($connection);
$userName = is_string($user) ? $user : $user->getUsername();
if ($event['type'] === 'REQ' || $event['type'] === 'RESP') {
$reqUser = $this->clientManipulator->findByUsername($topic, $event['username']);
/** user is still online */
if (false !== $reqUser) {
$topic->broadcast(
array(
'type' => $event['type'],
'user' => $this->findUser($topic, $userName),
'gameAssoc' => $event['gameAssoc'],
),
array(),
array(
$reqUser['connection']->WAMP->sessionId
)
);
}
}
if ($event['type'] === 'SEARCH') {
$reqUser = $this->clientManipulator->findByUsername($topic, $userName);
/** user is still online */
if (false !== $reqUser) {
$topic->broadcast(
array(
'type' => $event['type'],
'users' => $this->findUsersForSearch($topic, $event['username']),
'gameAssoc' => true,
),
array(),
array(
$reqUser['connection']->WAMP->sessionId,
)
);
}
}
}
/**
* Like RPC is will use to prefix the channel
*
* @return string
*/
public function getName()
{
return 'userlist.topic';
}
/**
* @param $topic
* @param $username
*
* @return array
*/
private function findUser($topic, $username)
{
$this->reConnect();
$userRepo = $this->em->getRepository('JotunheimrUserBundle:User');
$user = $userRepo->findOneByUsername($username);
$currentOnline = $this->clientManipulator->findByUsername($topic, $username)['client'];
return base64_encode(json_encode(array(
'id' => $user->getFacebookId(),
'name' => $user->getRealName(),
'email' => $user->getEmail(),
'username' => $user->getUsername(),
'online' => $currentOnline !== null
)));
}
/**
* @param $topic
* @param $username
*
* @return array
*/
private function findUsersForSearch($topic, $username)
{
$this->reConnect();
$userRepo = $this->em->getRepository('JotunheimrUserBundle:User');
$users = $userRepo
->createQueryBuilder('o')
->where('o.username LIKE :username')
->andWhere('o.realName LIKE :realname')
->setParameter('username', "%{$username}%")
->setParameter('realname', "%{$username}%")
->setMaxResults(10)
->getQuery()
->getResult();
$usersAvailable = array();
foreach ($users as $item) {
$usersAvailable[] = array(
'id' => $item->getFacebookId(),
'name' => $item->getRealName(),
'email' => $item->getEmail(),
'username' => $item->getUsername(),
'online' => $this->clientManipulator->findByUsername($topic, $item->getUsername())['client'] !== null
);
}
return base64_encode(json_encode(
$usersAvailable
));
}
/**
* Find all users online/offline
*
* @param $topic
*
* @return array
*/
private function findAllUsers($topic)
{
$usersOnline = array();
$usersOffline = array();
$this->reConnect();
$userRepo = $this->em->getRepository('JotunheimrUserBundle:User');
$usersMax = 10;
foreach ($this->clientManipulator->getAll($topic) as $item) {
if (!$usersMax) {
break;
}
$user = $userRepo->findOneByEmail(
$item['client']->getEmail()
);
$usersOnline[] = array(
'id' => $user->getFacebookId(),
'name' => $user->getRealName(),
'email' => $user->getEmail(),
'username' => $user->getUsername(),
'online' => true
);
$usersMax--;
}
foreach ($userRepo->findBy(array(), array(), 20) as $item) {
$exists = is_numeric(array_search($item->getEmail(), array_column($usersOnline, 'email')));
if (!$exists) {
$usersOffline[] = array(
'id' => $item->getFacebookId(),
'name' => $item->getRealName(),
'email' => $item->getEmail(),
'username' => $item->getUsername(),
'online' => false
);
}
}
return array_merge($usersOnline, $usersOffline);
}
/**
* Handle prod MySQL timeout
*/
private function reConnect()
{
try {
$connection = $this->em->getConnection();
if (false === $connection->ping()) {
$connection->close();
$connection->connect();
}
} catch (PDOException $ex) {
throw new NotFoundHttpException();
}
}
}

823
var/SymfonyRequirements.php Normal file
View File

@@ -0,0 +1,823 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*
* Users of PHP 5.2 should be able to run the requirements checks.
* This is why the file and all classes must be compatible with PHP 5.2+
* (e.g. not using namespaces and closures).
*
* ************** CAUTION **************
*
* DO NOT EDIT THIS FILE as it will be overridden by Composer as part of
* the installation/update process. The original file resides in the
* SensioDistributionBundle.
*
* ************** CAUTION **************
*/
/**
* Represents a single PHP requirement, e.g. an installed extension.
* It can be a mandatory requirement or an optional recommendation.
* There is a special subclass, named PhpIniRequirement, to check a php.ini configuration.
*
* @author Tobias Schultze <http://tobion.de>
*/
class Requirement
{
private $fulfilled;
private $testMessage;
private $helpText;
private $helpHtml;
private $optional;
/**
* Constructor that initializes the requirement.
*
* @param bool $fulfilled Whether the requirement is fulfilled
* @param string $testMessage The message for testing the requirement
* @param string $helpHtml The help text formatted in HTML for resolving the problem
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
* @param bool $optional Whether this is only an optional recommendation not a mandatory requirement
*/
public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false)
{
$this->fulfilled = (bool) $fulfilled;
$this->testMessage = (string) $testMessage;
$this->helpHtml = (string) $helpHtml;
$this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText;
$this->optional = (bool) $optional;
}
/**
* Returns whether the requirement is fulfilled.
*
* @return bool true if fulfilled, otherwise false
*/
public function isFulfilled()
{
return $this->fulfilled;
}
/**
* Returns the message for testing the requirement.
*
* @return string The test message
*/
public function getTestMessage()
{
return $this->testMessage;
}
/**
* Returns the help text for resolving the problem.
*
* @return string The help text
*/
public function getHelpText()
{
return $this->helpText;
}
/**
* Returns the help text formatted in HTML.
*
* @return string The HTML help
*/
public function getHelpHtml()
{
return $this->helpHtml;
}
/**
* Returns whether this is only an optional recommendation and not a mandatory requirement.
*
* @return bool true if optional, false if mandatory
*/
public function isOptional()
{
return $this->optional;
}
}
/**
* Represents a PHP requirement in form of a php.ini configuration.
*
* @author Tobias Schultze <http://tobion.de>
*/
class PhpIniRequirement extends Requirement
{
/**
* Constructor that initializes the requirement.
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
* @param bool $optional Whether this is only an optional recommendation not a mandatory requirement
*/
public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false)
{
$cfgValue = ini_get($cfgName);
if (is_callable($evaluation)) {
if (null === $testMessage || null === $helpHtml) {
throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.');
}
$fulfilled = call_user_func($evaluation, $cfgValue);
} else {
if (null === $testMessage) {
$testMessage = sprintf('%s %s be %s in php.ini',
$cfgName,
$optional ? 'should' : 'must',
$evaluation ? 'enabled' : 'disabled'
);
}
if (null === $helpHtml) {
$helpHtml = sprintf('Set <strong>%s</strong> to <strong>%s</strong> in php.ini<a href="#phpini">*</a>.',
$cfgName,
$evaluation ? 'on' : 'off'
);
}
$fulfilled = $evaluation == $cfgValue;
}
parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional);
}
}
/**
* A RequirementCollection represents a set of Requirement instances.
*
* @author Tobias Schultze <http://tobion.de>
*/
class RequirementCollection implements IteratorAggregate
{
/**
* @var Requirement[]
*/
private $requirements = array();
/**
* Gets the current RequirementCollection as an Iterator.
*
* @return Traversable A Traversable interface
*/
public function getIterator()
{
return new ArrayIterator($this->requirements);
}
/**
* Adds a Requirement.
*
* @param Requirement $requirement A Requirement instance
*/
public function add(Requirement $requirement)
{
$this->requirements[] = $requirement;
}
/**
* Adds a mandatory requirement.
*
* @param bool $fulfilled Whether the requirement is fulfilled
* @param string $testMessage The message for testing the requirement
* @param string $helpHtml The help text formatted in HTML for resolving the problem
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null)
{
$this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false));
}
/**
* Adds an optional recommendation.
*
* @param bool $fulfilled Whether the recommendation is fulfilled
* @param string $testMessage The message for testing the recommendation
* @param string $helpHtml The help text formatted in HTML for resolving the problem
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null)
{
$this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true));
}
/**
* Adds a mandatory requirement in form of a php.ini configuration.
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
{
$this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false));
}
/**
* Adds an optional recommendation in form of a php.ini configuration.
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
{
$this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true));
}
/**
* Adds a requirement collection to the current set of requirements.
*
* @param RequirementCollection $collection A RequirementCollection instance
*/
public function addCollection(RequirementCollection $collection)
{
$this->requirements = array_merge($this->requirements, $collection->all());
}
/**
* Returns both requirements and recommendations.
*
* @return Requirement[]
*/
public function all()
{
return $this->requirements;
}
/**
* Returns all mandatory requirements.
*
* @return Requirement[]
*/
public function getRequirements()
{
$array = array();
foreach ($this->requirements as $req) {
if (!$req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns the mandatory requirements that were not met.
*
* @return Requirement[]
*/
public function getFailedRequirements()
{
$array = array();
foreach ($this->requirements as $req) {
if (!$req->isFulfilled() && !$req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns all optional recommendations.
*
* @return Requirement[]
*/
public function getRecommendations()
{
$array = array();
foreach ($this->requirements as $req) {
if ($req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns the recommendations that were not met.
*
* @return Requirement[]
*/
public function getFailedRecommendations()
{
$array = array();
foreach ($this->requirements as $req) {
if (!$req->isFulfilled() && $req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns whether a php.ini configuration is not correct.
*
* @return bool php.ini configuration problem?
*/
public function hasPhpIniConfigIssue()
{
foreach ($this->requirements as $req) {
if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) {
return true;
}
}
return false;
}
/**
* Returns the PHP configuration file (php.ini) path.
*
* @return string|false php.ini file path
*/
public function getPhpIniConfigPath()
{
return get_cfg_var('cfg_file_path');
}
}
/**
* This class specifies all requirements and optional recommendations that
* are necessary to run the Symfony Standard Edition.
*
* @author Tobias Schultze <http://tobion.de>
* @author Fabien Potencier <fabien@symfony.com>
*/
class SymfonyRequirements extends RequirementCollection
{
const LEGACY_REQUIRED_PHP_VERSION = '5.3.3';
const REQUIRED_PHP_VERSION = '5.5.9';
/**
* Constructor that initializes the requirements.
*/
public function __construct()
{
/* mandatory requirements follow */
$installedPhpVersion = phpversion();
$requiredPhpVersion = $this->getPhpRequiredVersion();
$this->addRecommendation(
$requiredPhpVersion,
'Vendors should be installed in order to check all requirements.',
'Run the <code>composer install</code> command.',
'Run the "composer install" command.'
);
if (false !== $requiredPhpVersion) {
$this->addRequirement(
version_compare($installedPhpVersion, $requiredPhpVersion, '>='),
sprintf('PHP version must be at least %s (%s installed)', $requiredPhpVersion, $installedPhpVersion),
sprintf('You are running PHP version "<strong>%s</strong>", but Symfony needs at least PHP "<strong>%s</strong>" to run.
Before using Symfony, upgrade your PHP installation, preferably to the latest version.',
$installedPhpVersion, $requiredPhpVersion),
sprintf('Install PHP %s or newer (installed version is %s)', $requiredPhpVersion, $installedPhpVersion)
);
}
$this->addRequirement(
version_compare($installedPhpVersion, '5.3.16', '!='),
'PHP version must not be 5.3.16 as Symfony won\'t work properly with it',
'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)'
);
$this->addRequirement(
is_dir(__DIR__.'/../vendor/composer'),
'Vendor libraries must be installed',
'Vendor libraries are missing. Install composer following instructions from <a href="http://getcomposer.org/">http://getcomposer.org/</a>. '.
'Then run "<strong>php composer.phar install</strong>" to install them.'
);
$cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache';
$this->addRequirement(
is_writable($cacheDir),
'app/cache/ or var/cache/ directory must be writable',
'Change the permissions of either "<strong>app/cache/</strong>" or "<strong>var/cache/</strong>" directory so that the web server can write into it.'
);
$logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs';
$this->addRequirement(
is_writable($logsDir),
'app/logs/ or var/logs/ directory must be writable',
'Change the permissions of either "<strong>app/logs/</strong>" or "<strong>var/logs/</strong>" directory so that the web server can write into it.'
);
if (version_compare($installedPhpVersion, '7.0.0', '<')) {
$this->addPhpIniRequirement(
'date.timezone', true, false,
'date.timezone setting must be set',
'Set the "<strong>date.timezone</strong>" setting in php.ini<a href="#phpini">*</a> (like Europe/Paris).'
);
}
if (false !== $requiredPhpVersion && version_compare($installedPhpVersion, $requiredPhpVersion, '>=')) {
$timezones = array();
foreach (DateTimeZone::listAbbreviations() as $abbreviations) {
foreach ($abbreviations as $abbreviation) {
$timezones[$abbreviation['timezone_id']] = true;
}
}
$this->addRequirement(
isset($timezones[@date_default_timezone_get()]),
sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()),
'Your default timezone is not supported by PHP. Check for typos in your <strong>php.ini</strong> file and have a look at the list of deprecated timezones at <a href="http://php.net/manual/en/timezones.others.php">http://php.net/manual/en/timezones.others.php</a>.'
);
}
$this->addRequirement(
function_exists('iconv'),
'iconv() must be available',
'Install and enable the <strong>iconv</strong> extension.'
);
$this->addRequirement(
function_exists('json_encode'),
'json_encode() must be available',
'Install and enable the <strong>JSON</strong> extension.'
);
$this->addRequirement(
function_exists('session_start'),
'session_start() must be available',
'Install and enable the <strong>session</strong> extension.'
);
$this->addRequirement(
function_exists('ctype_alpha'),
'ctype_alpha() must be available',
'Install and enable the <strong>ctype</strong> extension.'
);
$this->addRequirement(
function_exists('token_get_all'),
'token_get_all() must be available',
'Install and enable the <strong>Tokenizer</strong> extension.'
);
$this->addRequirement(
function_exists('simplexml_import_dom'),
'simplexml_import_dom() must be available',
'Install and enable the <strong>SimpleXML</strong> extension.'
);
if (function_exists('apc_store') && ini_get('apc.enabled')) {
if (version_compare($installedPhpVersion, '5.4.0', '>=')) {
$this->addRequirement(
version_compare(phpversion('apc'), '3.1.13', '>='),
'APC version must be at least 3.1.13 when using PHP 5.4',
'Upgrade your <strong>APC</strong> extension (3.1.13+).'
);
} else {
$this->addRequirement(
version_compare(phpversion('apc'), '3.0.17', '>='),
'APC version must be at least 3.0.17',
'Upgrade your <strong>APC</strong> extension (3.0.17+).'
);
}
}
$this->addPhpIniRequirement('detect_unicode', false);
if (extension_loaded('suhosin')) {
$this->addPhpIniRequirement(
'suhosin.executor.include.whitelist',
create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'),
false,
'suhosin.executor.include.whitelist must be configured correctly in php.ini',
'Add "<strong>phar</strong>" to <strong>suhosin.executor.include.whitelist</strong> in php.ini<a href="#phpini">*</a>.'
);
}
if (extension_loaded('xdebug')) {
$this->addPhpIniRequirement(
'xdebug.show_exception_trace', false, true
);
$this->addPhpIniRequirement(
'xdebug.scream', false, true
);
$this->addPhpIniRecommendation(
'xdebug.max_nesting_level',
create_function('$cfgValue', 'return $cfgValue > 100;'),
true,
'xdebug.max_nesting_level should be above 100 in php.ini',
'Set "<strong>xdebug.max_nesting_level</strong>" to e.g. "<strong>250</strong>" in php.ini<a href="#phpini">*</a> to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.'
);
}
$pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null;
$this->addRequirement(
null !== $pcreVersion,
'PCRE extension must be available',
'Install the <strong>PCRE</strong> extension (version 8.0+).'
);
if (extension_loaded('mbstring')) {
$this->addPhpIniRequirement(
'mbstring.func_overload',
create_function('$cfgValue', 'return (int) $cfgValue === 0;'),
true,
'string functions should not be overloaded',
'Set "<strong>mbstring.func_overload</strong>" to <strong>0</strong> in php.ini<a href="#phpini">*</a> to disable function overloading by the mbstring extension.'
);
}
/* optional recommendations follow */
if (file_exists(__DIR__.'/../vendor/composer')) {
require_once __DIR__.'/../vendor/autoload.php';
try {
$r = new ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle');
$contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php');
} catch (ReflectionException $e) {
$contents = '';
}
$this->addRecommendation(
file_get_contents(__FILE__) === $contents,
'Requirements file should be up-to-date',
'Your requirements file is outdated. Run composer install and re-check your configuration.'
);
}
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.4', '>='),
'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions',
'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.8', '>='),
'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156',
'Install PHP 5.3.8 or newer if your project uses annotations.'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.4.0', '!='),
'You should not use PHP 5.4.0 due to the PHP bug #61453',
'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.4.11', '>='),
'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)',
'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.'
);
$this->addRecommendation(
(version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<'))
||
version_compare($installedPhpVersion, '5.4.8', '>='),
'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909',
'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.'
);
if (null !== $pcreVersion) {
$this->addRecommendation(
$pcreVersion >= 8.0,
sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion),
'<strong>PCRE 8.0+</strong> is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.'
);
}
$this->addRecommendation(
class_exists('DomDocument'),
'PHP-DOM and PHP-XML modules should be installed',
'Install and enable the <strong>PHP-DOM</strong> and the <strong>PHP-XML</strong> modules.'
);
$this->addRecommendation(
function_exists('mb_strlen'),
'mb_strlen() should be available',
'Install and enable the <strong>mbstring</strong> extension.'
);
$this->addRecommendation(
function_exists('iconv'),
'iconv() should be available',
'Install and enable the <strong>iconv</strong> extension.'
);
$this->addRecommendation(
function_exists('utf8_decode'),
'utf8_decode() should be available',
'Install and enable the <strong>XML</strong> extension.'
);
$this->addRecommendation(
function_exists('filter_var'),
'filter_var() should be available',
'Install and enable the <strong>filter</strong> extension.'
);
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->addRecommendation(
function_exists('posix_isatty'),
'posix_isatty() should be available',
'Install and enable the <strong>php_posix</strong> extension (used to colorize the CLI output).'
);
}
$this->addRecommendation(
extension_loaded('intl'),
'intl extension should be available',
'Install and enable the <strong>intl</strong> extension (used for validators).'
);
if (extension_loaded('intl')) {
// in some WAMP server installations, new Collator() returns null
$this->addRecommendation(
null !== new Collator('fr_FR'),
'intl extension should be correctly configured',
'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.'
);
// check for compatible ICU versions (only done when you have the intl extension)
if (defined('INTL_ICU_VERSION')) {
$version = INTL_ICU_VERSION;
} else {
$reflector = new ReflectionExtension('intl');
ob_start();
$reflector->info();
$output = strip_tags(ob_get_clean());
preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches);
$version = $matches[1];
}
$this->addRecommendation(
version_compare($version, '4.0', '>='),
'intl ICU version should be at least 4+',
'Upgrade your <strong>intl</strong> extension with a newer ICU version (4+).'
);
if (class_exists('Symfony\Component\Intl\Intl')) {
$this->addRecommendation(
\Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion(),
sprintf('intl ICU version installed on your system is outdated (%s) and does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
'To get the latest internationalization data upgrade the ICU system package and the intl PHP extension.'
);
if (\Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion()) {
$this->addRecommendation(
\Symfony\Component\Intl\Intl::getIcuDataVersion() === \Symfony\Component\Intl\Intl::getIcuVersion(),
sprintf('intl ICU version installed on your system (%s) does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
'To avoid internationalization data inconsistencies upgrade the symfony/intl component.'
);
}
}
$this->addPhpIniRecommendation(
'intl.error_level',
create_function('$cfgValue', 'return (int) $cfgValue === 0;'),
true,
'intl.error_level should be 0 in php.ini',
'Set "<strong>intl.error_level</strong>" to "<strong>0</strong>" in php.ini<a href="#phpini">*</a> to inhibit the messages when an error occurs in ICU functions.'
);
}
$accelerator =
(extension_loaded('eaccelerator') && ini_get('eaccelerator.enable'))
||
(extension_loaded('apc') && ini_get('apc.enabled'))
||
(extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable'))
||
(extension_loaded('Zend OPcache') && ini_get('opcache.enable'))
||
(extension_loaded('xcache') && ini_get('xcache.cacher'))
||
(extension_loaded('wincache') && ini_get('wincache.ocenabled'))
;
$this->addRecommendation(
$accelerator,
'a PHP accelerator should be installed',
'Install and/or enable a <strong>PHP accelerator</strong> (highly recommended).'
);
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$this->addRecommendation(
$this->getRealpathCacheSize() >= 5 * 1024 * 1024,
'realpath_cache_size should be at least 5M in php.ini',
'Setting "<strong>realpath_cache_size</strong>" to e.g. "<strong>5242880</strong>" or "<strong>5M</strong>" in php.ini<a href="#phpini">*</a> may improve performance on Windows significantly in some cases.'
);
}
$this->addPhpIniRecommendation('short_open_tag', false);
$this->addPhpIniRecommendation('magic_quotes_gpc', false, true);
$this->addPhpIniRecommendation('register_globals', false, true);
$this->addPhpIniRecommendation('session.auto_start', false);
$this->addRecommendation(
class_exists('PDO'),
'PDO should be installed',
'Install <strong>PDO</strong> (mandatory for Doctrine).'
);
if (class_exists('PDO')) {
$drivers = PDO::getAvailableDrivers();
$this->addRecommendation(
count($drivers) > 0,
sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'),
'Install <strong>PDO drivers</strong> (mandatory for Doctrine).'
);
}
}
/**
* Loads realpath_cache_size from php.ini and converts it to int.
*
* (e.g. 16k is converted to 16384 int)
*
* @return int
*/
protected function getRealpathCacheSize()
{
$size = ini_get('realpath_cache_size');
$size = trim($size);
$unit = '';
if (!ctype_digit($size)) {
$unit = strtolower(substr($size, -1, 1));
$size = (int) substr($size, 0, -1);
}
switch ($unit) {
case 'g':
return $size * 1024 * 1024 * 1024;
case 'm':
return $size * 1024 * 1024;
case 'k':
return $size * 1024;
default:
return (int) $size;
}
}
/**
* Defines PHP required version from Symfony version.
*
* @return string|false The PHP required version or false if it could not be guessed
*/
protected function getPhpRequiredVersion()
{
if (!file_exists($path = __DIR__.'/../composer.lock')) {
return false;
}
$composerLock = json_decode(file_get_contents($path), true);
foreach ($composerLock['packages'] as $package) {
$name = $package['name'];
if ('symfony/symfony' !== $name && 'symfony/http-kernel' !== $name) {
continue;
}
return (int) $package['version'][1] > 2 ? self::REQUIRED_PHP_VERSION : self::LEGACY_REQUIRED_PHP_VERSION;
}
return false;
}
}

View File

@@ -270,7 +270,7 @@ $hasMinorProblems = (bool) count($minorProblems);
} }
.sf-reset ul a, .sf-reset ul a,
.sf-reset ul a:hover { .sf-reset ul a:hover {
background: url(../images/blue-arrow.png) no-repeat right 6px; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFdJREFUeNpiYACBjjOhDEiACSggCKTLgXQ5TJARqhIkcReIKxgqTGYxwvV0nDEGkmeAOIwJySiQ4HsgvseIpGo3ELsCtZ9lRDIvDCiwhwHJPEFkJwEEGACq6hdnax8y1AAAAABJRU5ErkJggg==) no-repeat right 7px;
padding-right: 10px; padding-right: 10px;
} }
.sf-reset ul, ol { .sf-reset ul, ol {

View File

@@ -1,8 +1,8 @@
'use strict'; 'use strict';
var webpack = require("webpack"); let webpack = require("webpack");
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
var isProd = (process.env.NODE_ENV === 'production'); let isProd = (process.env.NODE_ENV === 'production');
/** /**
* Conditionally return a list of plugins to use based on the current environment. * Conditionally return a list of plugins to use based on the current environment.
@@ -10,7 +10,7 @@ var isProd = (process.env.NODE_ENV === 'production');
* @returns {Array} * @returns {Array}
*/ */
function getPlugins() { function getPlugins() {
var plugins = []; let plugins = [];
/** /**
* Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV` * Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
@@ -33,10 +33,14 @@ function getPlugins() {
return plugins; return plugins;
} }
const config = module.exports = { const config = {
module: {}
};
let mineSeekerConfig = Object.assign({}, config, {
entry: './web/bundles/mineseeker/js/mine-seeker.js', entry: './web/bundles/mineseeker/js/mine-seeker.js',
output: { output: {
path: './src/Mine/SeekerBundle/Resources/public/js', path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
filename: 'index.min.js' filename: 'index.min.js'
}, },
module: { module: {
@@ -46,12 +50,34 @@ const config = module.exports = {
exclude: /node_modules/, exclude: /node_modules/,
loader: 'babel', loader: 'babel',
query: { query: {
presets: ['es2015', 'react'] presets: ['es2015', 'es2016', 'es2017', 'react']
} }
} }
] ]
}, }
plugins: getPlugins() });
};
module.exports = config; let mineUserListConfig = Object.assign({}, config, {
entry: './web/bundles/mineseeker/js/mine-user-list.js',
output: {
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-user-list/',
filename: 'index.min.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'es2016', 'es2017', 'react']
}
}
]
}
});
module.exports = [
mineSeekerConfig,
mineUserListConfig,
];

View File

@@ -1,9 +1,13 @@
'use strict'; 'use strict';
const config = module.exports = { const config = {
module: {}
};
let mineSeekerConfig = Object.assign({}, config, {
entry: './web/bundles/mineseeker/js/mine-seeker.js', entry: './web/bundles/mineseeker/js/mine-seeker.js',
output: { output: {
path: './src/Mine/SeekerBundle/Resources/public/js', path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
filename: 'index.js' filename: 'index.js'
}, },
module: { module: {
@@ -13,11 +17,34 @@ const config = module.exports = {
exclude: /node_modules/, exclude: /node_modules/,
loader: 'babel', loader: 'babel',
query: { query: {
presets: ['es2015', 'react'] presets: ['es2015', 'es2016', 'es2017', 'react']
} }
} }
] ]
} }
}; });
module.exports = config; let mineUserListConfig = Object.assign({}, config, {
entry: './web/bundles/mineseeker/js/mine-user-list.js',
output: {
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-user-list/',
filename: 'index.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'es2016', 'es2017', 'react']
}
}
]
}
});
module.exports = [
mineSeekerConfig,
mineUserListConfig,
];