YAWIK documentation!

Contact:contact@yawik.org
Revision:0.34.5
Date:Jun 25, 2020

Contents:

About YAWIK

YAWIK offers a web based solution for managing job applications. Jobs ads can be entered or pushed to the system. The system assigns application forms to job ads. Applicants and Recruiters can connect to YAWIK using social networks.

So what is YAWIK?

YAWIK is a modular system for human resources. It can be used as a job board, as a simple data entry tool for job openings or as an application management system. It should give applicants the opportunity to quickly and easily create a Hire-Me-Page. Currently it is possible to integrate YAWIK into a corporate website by extending it with an own module (see Customize). On the long term it is designed to become a distributed system for connecting recruiters and applicants.

YAWIK is a PHP web application. It’s based on Laminas and mongo. The target group of YAWIK are companies and candidates.

As started in 2013, YAWIK is quite new but stable enough to be used by aprox 20 companies to manage applications.

Why do we do this?

We believe that:

  • Candidates should be able to easily apply to a job advertisement
  • Candidates should have sovereignty over their application data
  • Recruiters should be able to easily find candidates
  • Open Source and Human Resources fits together

How came YAWIK to be?

YAWIK was initiated by Carsten Bleek, owner of “CROSS Solution”. “CROSS Solution” was able to convince customers about the YAWIK idea. An initial group of [sponsors](https://yawik.org/sponsoren/) was found, and YAWIK was born.

Requirements

  • php >= php 7.2
  • Laminas
  • mongodb >= 3.*
  • php-mongodb >= 1.7
  • php-intl
  • php-gd
  • php-curl (only needed to install dependencies via composer)
  • php-dom (only needed to install dependencies via composer)
  • php-openssl (only needed to install dependencies via composer)
  • php-mbstring (only needed, if the PDF module is used)

YAWIK should run on any OS, which supports the above software components. In real life, we’ve seen YAWIK running on Linux Ubuntu, Debian, FreeBSD and OSX. It’s possible to run YAWIK on AWS.

On FreeBSD, make sure, the php fileinfo extention is available. Fileinfo extention is needed by validating file uploads.

The YAWIK development happens under mainly Ubuntu.

Preparations

YAWIK needs PHP >=7.2 for execution and the described extensions from the requirements.

For the installation via Composer (this is the easiest way at the moment) npm is needed. The Nodes Package Manager executes grunt tasks at the end of the installation which copy images, convert LESS to CSS and compress JS.

Data is stored in a MongoDB. The easiest way is to install a MongoDB locally. If this is not possible, you can use a MongoDB provider like mlab.com or google.

Apache or nginx can be used as webserver. For testing you can use the PHP buildin server.

And of course you need composer.

In the different Linux distributions there are dirverse differences. So you have to proceed differently until an installation via composer works.

Ubuntu 18.04

Installation of PHP7.2 and apache2. Ubuntu 18.04 comes with php7.2.3 and ext-mongodb 1.3.4. You’ll need at least ext-mongodb ^1.5.0. You’ll have to build it from PECL by yourself or use the great ondrej repos.

sudo apt install software-properties-common
add-apt-repository ppa:ondrej/php

install npm version 10. It’s needed to run grunt tasks at the end of the installation.

apt install curl
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs

If you want to run php7.2

apt install php-mongodb php7.2 php7.2-curl php7.2-xsl php7.2-intl php7.2-common php7.2-cli php7.2-json php7.2-gd curl libapache2-mod-php7.2 \
    php7.2-cli apache2 php7.2-xml php7.2-mbstring composer unzip git

With php7.3 we’ve noticed an issue which leads to crashes in the apache module. At least in LXC containers.

Debian 10

Debian 10 comes with PHP7.3 by default.

sudo apt install php-mongodb libapache2-mod-php php-curl php-gd php-intl php-json php-dom curl gzip git php composer npm

This installs everything to install YAWIK via composer.

Note

npm is only needed because at the end of the installation a few grunt tasks copy images, generate CSS and compress JS. It’s a good idea not to install it the apt, but via https://github.com/nodesource/distributions

Install mongo Database

https://docs.mongodb.com/manual/installation/ YAWIK runs with mongo 2.4. So you can use the mongod version, which is shipped with your distribution. However, you should use a later version. Otherwise you have to enable the text search, which is disabled in 2.4 by default. In 2.6 and above the text search is enabled by default.

You can install e.g. mongo 3.2 by: (Our demo is running 3.2, development is done with 4.x)

https://docs.mongodb.com/manual/administration/install-on-linux/

We’ve installed mongo the following way:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
sudo apt-get update
sudo apt-get install -y mongodb-org

If your linux comes with systemd, you can start your mongod with service mongo start. If you need an init script, because your linux comes with sysv, you can fetch it from mongodb github repository

cd /etc/init.d/
curl https://raw.githubusercontent.com/mongodb/mongo/master/debian/init.d > mongod
chmod +x mongod
update-rc.d mongod defaults

Start your mongod with /etc/init.d/mongod start

Installation

YAWIK is a modular system. It can be used as a job board or as an applicant management system. If you want to change the layout, the navigation or the functionality, you have to build your own module.

Our yawik/standard can be used as a template for such a module.

Installation with composer

A YAWIK instance can be build with composer.

composer create-project yawik/standard path/to/yawik

This will install YAWIK with all development dependencies. You can add additional modules with

cd path/to/yawik
composer require [modulename]

For a list of available modules, check https://packagist.org/?type=yawik-module

If you do not want to have all development dependencies on your production server, you need to copy all files except the vendor directory and all directories under public from path/to/yawik to a new directory. In this directory you then run

composer install --no-dev

Alternatively you can create a new yawik project and copy the files config/modules.config.php and composer.lock from path/to/yawik. Then run the composer install

composer create-project --no-dev path/to/yawik-production
cd path/to/yawik-production
cp path/to/yawik/config/modules.config.php ./config
cp path/to/yawik/composer.lock path/to/yawik/composer.json .
composer install --no-dev

Finally you need to transfer the path/to/yawik-production to your webserver. For configuring apache to server YAWIK, please look in the section below. Document root must be the public directory.

Install without composer

Get the latest YAWIK Package from Sourceforge. Packages are build as ZIP or TGZ archive. They extract into a subdirectory YAWIK-x.y.z. If you preserve the permissions, the directories cache and log should be writable after extraction.

tar preserves permissions with the p-Option. So unpack a TGZ with tar -xzpf YAWIK-y.x.z.tgz. unzip preserves the permissions by default (at least on ubuntu 14.4). So unpack a ZIP archive with unzip YAWIK-x.y.z.zip

_images/install-step-2.png
_images/install-step-1.png

By pointing your browser to the YAWIK-x.y.z/public directory, an installation page appears. You’ll be asked to enter a mongodb connection string, a username, a password and an email address.

Note

YAWIK will run in production mode by default. So if you make modifications to the config autoload files you have to remove the cache/module-classmap-cache.module_map.php and cache/module-config-cache.production.php.

Install for Developers

if you want to modify the YAWIK code, you should clone the repository from Github. The repository does not contain any dependency. You have to import all dependencies by executing the ìnstall.sh script located in the YAWIK root. This scripts imports all external libraries via composer. In addition, it creates the directories log, cache ùnd config/autoload and set the directory permissions to a+w.

git clone https://github.com/cross-solution/YAWIK
cd YAWIK
./install.sh

After the execution you are ready to point your browser to the public directory. You’ll get the install wizard and after entering the initial user, the database connection and an email address you are ready to use YAWIK.

At this point your `config/autoload directory contains only one file yawik.config.global.php containing the database connection string. The initial user is created with the àdmin role in the database.

$ ls YAWIK/config/autoload
yawik.config.global.php

All other configurations are currently done manually by copying the `*.dist files from the modules configuration directory to the autoload directory and removing the “.dist” part.

Note

To disable the caching of the config autoload files you need to set an environment variable called APPLICATION_ENV to the value “development”

If you use apache, you can do this in your virtual section config with SetEnv APPLICATION_ENV="development"

Runtime

Using Apache

If you want to use Apache, you probably need root access to the machine you’ve installed YAWIK on. In addition you need to enable the rewrite module of apache.

sudo a2enmod rewrite && sudo service apache2 reload

Then you have to make sure that the DocumentRoot of apache is pointing to YAWIK/public and apache is allowed to Access the YAWIK directory.

A VirtualHost section might look like.

<VirtualHost *:80>
     ServerName yawik.example.com
     DocumentRoot /var/www/YAWIK/public
     AddDefaultCharset utf-8

     # set an env to disable caching.
     #SetEnv APPLICATION_ENV "development"

     <Directory /var/www/YAWIK/public>
          DirectoryIndex index.php
          Options Indexes FollowSymLinks MultiViews
          AllowOverride All
          # for apache >=2.4
          Require all granted

          # for apache <= 2.2
          # Allow from all
     </Directory>
 </VirtualHost>

Place this in a file called yawik.example.com.conf in /etc/apache2/conf and execute

sudo a2ensite yawik.example.com.conf && sudo service apache2 reload

now you should be able to login into your YAWIK by pointing a browser to

http://${YAWIK_HOST}

Note

Be sure you either export the variables YAWIK_HOST and YAWIK_HOME or replace them with the actual values in the apache config file.

Also your Webserver should not be able to access your build.properties. You can safely remove this file after you’ve run the installation is done.

Using Nginx

A configuration file for Nginx looks like this

server {
     listen         80;

      server_name my.yawik.host;

      root /your-location/YAWIK/public;
      index index.html index.htm index.php;
      charset utf-8;

      location / {
          try_files $uri $uri/ /index.php$is_args$args;
      }

      location ~ \.php$ {
          fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
          fastcgi_pass unix:/run/php/php5.6-fpm.sock;
          fastcgi_param   APPLICATION_ENV  production;
          include /etc/nginx/fastcgi_params;
      }
}

Todo

We need more details on setup nginx here. - Where to put the server config - What commands to run.

Yawik can be downloaded at https://sourceforge.net/projects/yawik/files/

Configuration

Configuration files are located in config/autoload. Config files are returning an associative array. All arrays are merged, so the order how the configuration files are processed is relevant.

Files with names ending in *.global.php are process first. As a second files ending in *.{env}.php. {env} can have at least the values production, and development. If the environment variable APPLICATION_ENV is set, and if files named *. development.php exist, then these configurations are processed. If no environment variable ist set, production is assumed.

At the end *.local.php files are processed.:

Modules are coming with there own config directory. Configuration files of modules can be named *.config.php. This allows you to split configurations into sections. E.g. a router.config.php file should contain an associative array defining routing specific things.

If the enviroment is set to production, all configurations are cached in cache/module-classmap-cache.module_map.php. There is currently no way to invalidate the cache. You have to remove this file, if you alter files in config/autoload.

Authentication

to enable login via Facebook, Xing, LinkedIn or any other hybridauth adapter simply copy the module.auth.local.php.dist file to config/autoload/module.auth.local.php and adjust your keys and secrets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
return array(
     'hybridauth' => array(
     "Facebook" => array (
         "enabled" => true,
         "keys"    => array ( "id" => "", "secret" => "" ),
         "scope"       => 'email, user_about_me, user_birthday, user_hometown, user_website',
     ),
     "LinkedIn" => array (
         "enabled" => true,
         "keys"    => array ( "key" => "", "secret" => "" ),
     ),
     "XING" => array (
         "enabled" => true,
         "keys"    => array ( "key" => "", "secret" => "" ),
     ),
     "Github" => array(
         "enabled" => true,
         'keys'    => array ( "id" => "", 'secret' => ""),
         "scope"   => ''
     ),
     "Google" => array(
          "enabled" => true,
          'keys'    => array ( "id" => 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com', 'secret' => ''),
          "scope"   => 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
     ),
);
?>

Debugging

you can enable the debugging Mode by setting the environment variable APPLICATION_ENV=development. This will increase the debug level, enable error messages on the screen and disables sending of mails to the recipients, stored in the database. You can overwrite the the all recipients (To, CC, Bcc) by setting mail.develop.override_recipient=<your mail address>

Upgrade

composer update should do the task.

But: never forget to backup before upgrade.

1) backup your mongo data with the mongodump command. This will create a directory dump containing all your mongo databases. You can restore these databases with the mongorestore command.

YAWIK creates all needed mongo indexes automatically. But this only works, if an index is not already available. Since some indexes have changed in the past, it might be required to drop all indexes, so YAWIK will be able to create all needed indexes.

To drop all indexes, go to your mongo shell and type:

set1:PRIMARY> db.users.dropIndexes();
set1:PRIMARY> db.applications.dropIndexes();
set1:PRIMARY> db.jobs.dropIndexes();
  1. Move your YAWIK Installation to a new location, so you are able to undo the upgrade any time.

3) Install the new Version. Eighter via git or unpack the latest ZIP/TGZ Package from sourceforge. In contrast to a fresh installation, you do not access your updated YAWIK via a Browser. Copy all config/autoload/* files of your moved old YAWIK installation into to config/autoload directory of your new installation.

  1. Now you can access your new YAWIK via a Browser.

0.24 => 0.25

New users get a status. You can update old Users by

db.getCollection('users').update({'status': {$exists : false}}, {$set: {'status': {
    "name" : "active",
    "order" : NumberLong(50)
}}}, {multi: true})

Companynames ares searchable and sortable. If you want to make older companies searchable and sortable to, run the following query

db.getCollection('organizations.names').find().forEach(function(name) {
    db.getCollection('organizations').update({organizationName: name._id}, {$set: {_organizationName: name.name}}, {multi: true});
})

0.31 => 0.32

0.32 requires to use the ext-mongodb extension. You have to replace your ext-mongo by ext-mongodb.

Configuration

Configuration files are located in config/autoload. Config files are returning an associative array. All arrays are merged, so the order how the configuration files are processed is relevant.

Files with names ending in *.global.php are process first. As a second files ending in *.{env}.php. {env} can have at least the values production, and development. If the environment variable APPLICATION_ENV is set, and if files named *. development.php exist, then these configurations are processed. If no environment variable ist set, production is assumed.

At the end *.local.php files are processed.

Modules are coming with there own config directory. Configuration files of modules can be named *.config.php. This allows you to split configurations into sections. E.g. a router.config.php file should contain an associative array defining routing specific things.

If the enviroment is set to production, all configurations are cached in cache/module-classmap-cache.module_map.php and cache/module-config-cache.production.php. There is currently no way to invalidate the cache. You have to remove these files, if you modify files in file:config/autoload.

Authentication

to enable login via Facebook, Xing, LinkedIn or any other hybridauth adapter simply copy the module.auth.local.php.dist file to config/autoload/module.auth.local.php and adjust your keys and secrets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
return array(
     'hybridauth' => array(
     "Facebook" => array (
         "enabled" => true,
         "keys"    => array ( "id" => "", "secret" => "" ),
         "scope"       => 'email, user_about_me, user_birthday, user_hometown, user_website',
     ),
     "LinkedIn" => array (
         "enabled" => true,
         "keys"    => array ( "key" => "", "secret" => "" ),
     ),
     "XING" => array (
         "enabled" => true,
         "keys"    => array ( "key" => "", "secret" => "" ),
     ),
     "Github" => array(
         "enabled" => true,
         'keys'    => array ( "id" => "", 'secret' => ""),
         "scope"   => ''
     ),
     "Google" => array(
          "enabled" => true,
          'keys'    => array ( "id" => 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com', 'secret' => ''),
          "scope"   => 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
     ),
);
?>

Example: Setting up Facebook, Xing or LinkedIn Login

YAWIK$ cp module/Auth/config/module.auth.global.php.dist config/autoload/module.auth.global.php

All placeholders in the configuration files which match ‘%%.*%%’ are deprecated. They are relics of the build.properties area. Since 0.20 an intall wizard is available which introduces an initial user with the admin role.

....
"keys"    => array ( "id" => "%%facebook.appid%%", "secret" => "%%facebook.secret%%" ),
....

Note: you need a Facebook, Xing or LinkedIn App, if you want to integrate the social networks . So take a look how to create an App with Facebook, Xing or LinkedIn.

Copy the .dist files from the modules//config dir into the config/autoload directory. Don’t forget to remove the “.dist” suffix. Addjust the values and remove the cache/modules- files.

Authentication

to enable login via Facebook, Xing, LinkedIn or any other hybridauth adapter simply copy the module.auth.local.php.dist file to config/autoload/module.auth.local.php and adjust your keys and secrets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    <?php
    return array(
         "Facebook" => array (
             "enabled" => false,
             "keys"    => array ( "id" => "your-consumer-key", "secret" => "your-consumer-secret" ),
             "scope"   => "email, user_about_me, user_birthday, user_hometown, user_work_history, user_education_history",// optional
             "display" => "popup"
         ),
         "LinkedIn" => array (
             "enabled" => true,
             "keys"    => array ( "id" => "your-consumer-key", "secret" => "your-consumer-secret" ),
             "scope"   => "r_fullprofile, r_emailaddress"
         ),
         "XING" => array(
             "enabled" => true,
             'keys'    => array ( "key" => 'your-consumer-key', 'secret' => 'your-consumer-secret'),
             "scope"   => ''
         ),
         "Github" => array(
             "enabled" => true,
             'keys'    => array ( "id" => 'your-consumer-key', 'secret' => 'your-consumer-secret'),
             "scope"   => ''
         ),
         "Google" => array(
              "enabled" => true,
              'keys'    => array ( "id" => 'your-consumer-key', 'secret' => 'your-consumer-secret'),
              "scope"   => 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',

     ),
    );
    ?>

The configuration structure was simply taken from the hybridauth library. So the “enabled” field means enabled for the hybridauth library. It does not mean “enabled” for login. To enable a social network for login you have to ad the lowercased key to enableLogins array. You have to copy the auth.options.global.php.dist to config/autoload/auth.options.global.php and adjust your values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
     $options = array(
             /*
             * default email address, which is used in FROM headers of system mails like "new registration",
             * "forgot password",..
             */
             'fromEmail' => 'email@example.com',

             /*
             * default name address, which is used in FROM headers of system mails like "new registration",
             * "forgot password",..
             */
             'fromName' => 'YAWIK Website',

             /*
             * Subject of your registration Mail
             */
             'mailSubjectRegistration' => 'your registration',

             /*
              * enable social networks for login and registration. The names must match the keys used in
              * in the 'hybridauth' section of you module.auth.global.php file
              */
              'enableLogins' => ['linkedin','github','xing','google','facebook'],

             /*
              * if true, users are allowed to register.
              */
              'enableRegistration' => true,

             /*
              * if true, users can reset their password.
              */
              'enableResetPassword' => true,
     );

Mail

To configure an SMTP Server, copy MailServiceOptions.config.local.php to your config/autoload directory and adjust the values.

Setting the senders address

copy the auth.options.global.php into your config/autoload and set the fromEmail value.

Setting Mail Texts

The mail texts are defined by the following templats. You can overwrite the mails by mapping the following keys

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
         'mail/header' => __DIR__ . '/../view/mail/header.phtml',
         'mail/footer' => __DIR__ . '/../view/mail/footer.phtml',
         'mail/footer.en' => __DIR__ . '/../view/mail/footer.en.phtml',
         'mail/forgotPassword' =>  __DIR__ . '/../view/mail/forgot-password.phtml',
         'mail/register' =>  __DIR__ . '/../view/mail/register.phtml',
         'mail/job-created' => __DIR__ . '/../view/mails/job-created.phtml',
         'mail/job-pending' => __DIR__ . '/../view/mails/job-pending.phtml',
         'mail/job-accepted' => __DIR__ . '/../view/mails/job-accepted.phtml',
         'mail/job-rejected' => __DIR__ . '/../view/mails/job-rejected.phtml',
         'auth/mail/new-registration' => __DIR__ . '/../view/mail/new-registration.phtml',
         'auth/mail/user-confirmed' => __DIR__ . '/../view/mail/user-confirmed.phtml',
         'jobs-by-mail/form/subscribe/form' => __DIR__ . '/../view/jobs-by-mail/form.phtml',
         'jobs-by-mail/mail/jobs' => __DIR__ . '/../view/mail/jobs.phtml',
         'jobs-by-mail/mail/confirmation' => __DIR__ . '/../view/mail/confirmation.phtml',

The mail texts can be translated by adding the languages to the mapping keys. The Logic is coded in: https://github.com/cross-solution/YAWIK/blob/develop/module/Core/src/Core/Mail/HTMLTemplateMessage.php#L246

1
2
3
4
             'mail/job-created.fr' => __DIR__ . '/../view/mails/job-created.fr.phtml',
             'mail/job-pending.fr' => __DIR__ . '/../view/mails/job-pending.fr.phtml',
             'mail/job-accepted.fr' => __DIR__ . '/../view/mails/job-accepted.fr.phtml',
             'mail/job-rejected.fr' => __DIR__ . '/../view/mails/job-rejected.fr.phtml',

Jobs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
     $options = array(

             /**
             * If not set, the email address of the default user is used
             * @see Jobs\Options\ModulesOptionFactory
             */
             'multipostingApprovalMail' => '',

             /**
             * If a target Uri is set, a rest Request is sent to this target in case
             * a job posting was accepted.
             */
             'multipostingTargetUri' => '',

             /**
             * default Logo, if a company has no logo.
             */
             'default_logo' => '/Jobs/images/yawik-small.jpg',

             /**
             * Maximum size in bytes of a company Logo
             */
             'companyLogoMaxSize' => 100000,

             /**
             * Allowed Mime-Types for company Logos
             */
             'companyLogoMimeType' => array("image")
     );

     ### do not edit below ###

     return array('jobs_options' => $options);

Setting channels

Currently prices and channels are hard coded. The operator of YAWIK is responsible for publishing a jobposting to n ordered channel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
     $channel['yawik'] = array(
             'label' => 'YAWIK',
             'prices' => [ 'base' => 99, 'list' => 99, 'min'  => 99, ],
             'headline' => /*@translate*/ 'publish your job on yawik.org for free',
             'description' => /*@translate*/ 'publish the job for 30 days on %s',
             'linktext' => /*@translate*/ 'yawik.org',
             'route' => 'lang/content',
             'publishDuration' => 60,
             'params' => array(
                 'view' => 'jobs-publish-on-yawik'
             )
         );

     $channel['jobsintown'] = array(
             'label' => 'Jobsintown',
             'prices' => [ 'base' => 650, 'list' => 698, 'min'  => 499, ],
             'headline' => '30 Tage, incl. Karrierenetzwerk',
             'description' => 'publish the job for 30 days on %s',
             'linktext' => 'www.jobsintown.de',
             'logo' => '/Jobs/images/channels/jobsintown.png',
             'route' => 'lang/content',
             'publishDuration' => 30,
             'params' => array(
                 'view' => 'jobs-publish-on-jobsintown'
             )
         );

     $channel['fazjob'] = array(
             'label' => 'FAZjob.NET',
             'prices' => [ 'base' => 1095, 'list' => 1095, 'min'  => 1095, ],
             'headline' => '30 Tage auf dem Karriereportal der FAZ',
             'description' => 'publish the job for 30 days on %s',
             'linktext' => 'FAZjob.net',
             'logo' => '/Jobs/images/channels/fazjob_net.png',
             'route' => 'lang/content',
             'publishDuration' => 60,
             'params' => array(
                 'view' => 'jobs-publish-on-fazjob-net'
             )
         );

     $channel['homepage'] = array(
             'label' => /*@translate*/ 'Your Homepage',
             'prices' => [ 'base' => 0, 'list' => 0, 'min'  => 0, ],
             'headline' => /*@translate*/ 'enable integration of this job on your Homepage',
             'description' => /*@translate*/ 'enable %s of this job on your Homepage',
             'linktext' => /*@translate*/ 'integration',
             'route' => 'lang/content',
             'params' => array(
                 'view' => 'jobs-publish-on-homepage'
             )
         );

     return array('multiposting'=> array('channels' => $channel));

Sitename

Apache

point the DocumentRoot of your Webserver to the public directory.

<VirtualHost *:80>
      ServerName YOUR.HOSTNAME
      DocumentRoot /YOUR/DIRECTORY/YAWIK/public

      <Directory /YOUR/DIRECTORY/YAWIK/public>
              DirectoryIndex index.php
              AllowOverride All
              Order allow,deny
              Allow from all
      </Directory>
</VirtualHost>

Note

you should SetEnv APPLICATION_ENV development in your VirtualHost section, if you plan do develop.

MongoDB

Debugging

You can enable the debugging mode by setting the following configuration:

'tracy' => [
    'mode' => false
]

This will enable displaying of error messages on the screen and disables sending of email error notifications to the recipients specified in the following configuration:

'tracy' => [
    'email' => 'first.recipient@domain.tld, second.recipient@domain.tld'
],

The complete debugging configuration with default values consists of:

'tracy' => [
    'mode' => true, // toggles the production/development mode (true = production, false = development, null = autodetect, IP address(es) csv/array)
    'bar' => false, // toggles the diagnostics bar (a small bar placed in the bottom right corner of a browser displaying system info such a memory usage, elapsed time, ...)
    'strict' => true, // sets the error level strictness (bool = cause immediate death, int = matched against error severity)
    'log' => __DIR__ . '/../../../log/tracy', // sets the path to a log directory (this directory keeps error.log, snoozing mailsent file & html exception trace files)
    'email' => null, // sets the recipient(s) of email error notifications in the production mode (multiple recipients are separated with a comma or presented as an array)
    'email_snooze' => 900 // sets the interval for sending email error notifications in seconds
],

Debugging Mails

To send all mails to one or more specific mail addresses, regardless of the original recipients in To:, Cc: and Bcc: headers, you can set the configuration:

'mails' => array(
    'develop' => array(
            /*
             * Every mail send from the system will be send ONLY to this address(es),
             * regardless of the original set recipient(s) (including CC and BCC).
             * Use comma to separate addresses.
             * Use an empty string (or comment out) to disable.
             */
            'override_recipient' => '',

    ),
),

If you put this configuration in an autoloaded config file which name ends in .development.php, it will be loaded only, when the environment variable APPLICATION_ENV is set to “development”. This allows for quick enabling and disabling without the need to modify configuration files.

Modules

we use module system of the ZF2. Modules are configured in their config directory. You can use multiple configuration files by using the \Core\ModuleManager\ModuleConfigLoader utility. This way you can split up your configuration in smaller chunks (e.g. put all your configuration about routings into a router.config.php and about templating into a template.congig.php), which are easier to find, read and maintain.

Modules can simply be enabled by adding their names to an array in config/config.php.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
$modules = array(
      'DoctrineModule',
      'DoctrineMongoODMModule',
      'Core',
      'Auth',
      'Cv',
      'Applications',
      'Jobs',
      'Organizations',
      'Settings',
      'Pdf',
      'Geo'
 );

...
?>

Directory Structure of a module

directory description example
language contains gettext language files _images/module.png
public place for images, css or javascript
config place for configuration files
test place for unit tests
src Controllers, Models etc.
view view scripts

A module can implement the following Features:

  • Dashboard Widgets
  • Configuration formulars
  • Command line tools

currently the following modules exists:

Core


translation state of Core module.

Contents:

Assets

Assets are common JS libraries like jquery, bootstrap or select2. It makes sense to manage these assets by npm. This means, all needed JS libraries are listed in package.json. If an additional library is needed, it can be added via npm i --save-dev <packagename>. This will update the package.json and download the package to the node_modules directory. Our bin/install-assets.sh copies all needed javascript, css, fonts, etc. files to the public/assets directory, which is accessable by the web server.

You can download all required JS files and copy them to their location in the assets dir with:

npm install
bin/install-assets.sh

You can allways remove and reinstall assets with

rm -R public/assets/*
npm install
bin/install-assets.sh

this will copy all needed JS files into public/assets.

Formular Handling

Author:Mathias Gelhausen <gelhausen@cross-solution.de>

Forms are essential. YAWIK uses forms almost everywhere. The main goals of forms are:

  • Consistent look&feel
  • Binding to Entities
Build-In form classes and helpers

[TODO: Fill in.. ]

Form classes
\Core\Form\Form

This is the very base YAWIK form class, which extends \Zend\Form\Form.

It implements \Core\Form\DescriptionAwareFormInterface and \Core\Form\DisableElementsCapableInterface.

It sets the default hydrator to \Core\Entity\Hydrator\EntityHydrator (which allows the form to bind YAWIK entities.)

\Core\Form\BaseForm

An extension of \Core\Form\Form, which creates a form with a target fieldset and a default buttons fieldset.

It is meant to provide a ad-hoc solution for creating forms with a consistent look and handling.

\Core\Form\Container

The Container bundles several forms together, which work on one entity, enabling them to patch their behaviour together. Container have some specific methods for identifying or handle an explicit form. Most of these methods just pass some information to all subsequent forms

setParams(array $params)

is placing a hidden input filed in every subordinated forms. This comes in handy for the identification of an entitity.

setEntity(EntityInterface $entity)
Interfaces
View helpers
\Core\Form\View\Helper\FormContainer
\Core\Form\View\Helper\Form
\Core\Form\View\Helper\SummaryForm
Learning YAWIK forms

[TODO: Here must be some text….]

We try to make our forms’ look and feel consistent across the application.

Therefor there are many form classes and view helpers available to help creating, handling and rendering forms.

Form

All YAWIK forms are handled by one javascript file, unless it has the html attribute data-handle set to a value other than yk-forms. This javascript bind on the submit event, makes a ajax call to the forms’ action and takes care of displaying the error messages, if any. It then triggers an own event called yk.forms.done and passes the ajax call result to all listeners.

BaseForm

Most of our forms share a common structure:

Some elements (inputs), grouped optionally in fieldsets and the “Submit” and “Cancel” buttons at the bottom.

To simplify creation of such forms, there’s the \Core\Form\BaseForm class. You specify the fieldset with the elements, and the BaseForm adds a Button-Fieldset automatically.

The specified fieldset will be used as base fieldset of the form, so binding objects to the form will effectivly bind it to the fieldset. (see ZF-Doc)

Examples

Simplest BaseForm: Set the base fieldset to a form element manager service name.

class MyForm extends BaseForm
{
    protected $baseFieldset = 'MyFieldset';
}

Provide factory specification as base fieldset:

class MyForm extends BaseForm
{
    protected $baseFieldset = array(
        'type' => 'MyFieldset',
        'options' => array( /* ... */ ),
        /*...*/
    );
}

Overwrite parent methods to further customize:

class MyForm extends BaseForm
{
    protected function addBaseFieldset()
    {
        $fs = new Fieldset();
        /* configure your fieldset as you want */
        $fs->setUseAsBaseFieldset(true);
        $this->add($fs);
    }

    protected function addButtonsFieldset()
    {
        /* add the desired buttons fieldset here, e.g.: */
        $this->add(array(
            'type' => 'MyButtons',
        ));
    }
SummaryForm
_images/summary_form-form-mode.png

Form Mode

The most used form will be the SummaryForm, which is an extension of BaseForm, that lets you render a form in two presentation modes:

_images/summary_form-summary-mode.png

Summary Mode

Each presentation mode is rendered as a sub container in one html container, and one of these containers is hidden while the other is displayed.

The summary container gets a “edit”-button in the top right corner on hovering. Clicking this button will toggle the presentation modes. Clicking the submit or cancel button in the form will - in case of submit only if the results’ valid field is true - toggle also the presentation modes.

The view helper SummaryForm takes care of rendering the form and includes the necessary Javascript files.

Note: The summary presentation renders only the base fieldset.

If the SummaryForms’ base fieldset implements the \Core\Form\ViewPartialProviderInterface, it’s possible to provide a view partial for the fieldsets’ form view and summary view separately or provide one partial to render both modes. The view helper decides what to do on this criterions:

  • To render the form part: It appends .form to the partial name and tries to resolve this template name using the ViewResolver. If this template name can be resolved, it is used to render the form, if not, the template with the original name is used.
  • To render the summary part: It appends .view to the partial name and tries to resolve this template name using the ViewResolver. If this template name can be resolved, it is used to render the summary, if not, the template with the original name is used and a variable named “renderSummary” is passed with the boolean value “TRUE”.

If no view partial is provided, it loops over the elements of the form and renders the elements as list of element labels, element values pairs.

Prior to rendering, the activated mode can be set. (Either form or summary)

The summary form javascript expects a field “content” in the ajax call result (json), which holds the rendered summary. This content then replaces the old summary content.

Examples

Create a summary form:

class MyForm extends SummaryForm
{
    protected $baseFieldset = 'MyFieldset';
}

render in view:

$this->summaryForm($form);

Handle in controller:

public function myFormAction()
{
    $services = $this->getServiceManager();
    $forms    = $services->get('FormElementManager');
    $form     = $forms->get('MyForm');
    $request  = $this->getRequest();

    if ($request->isPost()) {
        $form->setData($request->getPost());
        if ($form->isValid()) {
            $helper = $services->get('ViewHelperManager')->get('summaryform');
            return new JsonModel(array(
                'valid' => true,
                'content' => $helper($form)
            ));
        } else {
            return new JsonModel(array(
                'valid' => false,
                'errors' => $form->getMessages(),
            ));
        }
    }

    return array(
        'form' => $form
    );

To render Using ViewPartialProviderInterface in a SummaryForm (remember to use the base fieldset to set the partial…)

class MyForm extends SummaryForm
{
    protected $baseFieldset = 'MyFieldset';
}

class MyFieldset extends Fieldset implements ViewPartialProviderInterface
{
    protected $partial = 'my-form';

    public function getViewPartial()
    {
        return $this->partial;
    }

    public function setViewPartial($partial)
    {
        $this->partial = $partial;
        return $this;
    }
}

Render both presentation modes in one partial “my-form.phtml”:

<?php if ($this->renderSummary): ?>
<!-- create the summary view, access the form with $this->form -->
<?php else: echo $this->summaryForm()->renderForm($this->form); ?>

Render the presentation modes in separate views “my-form.form.phtml” and “my-form.view.phtml”

<!-- my-form.form.phtml -->

<?php echo $this->summaryForm()->renderForm($this->form) ?>


<!-- my.form.view.phtml -->
<?php
    /* $this->renderSummary is NOT set, when using separate view scripts. */
    echo $this->summaryForm()->renderSummary($this->form)
?>
Container
Simple Form

Notifications

YAWIK comes with a notification system to easily display notification messages to the user. These messages are persisted in the session and can be retrieved even after a redirection (e.g. Login)

Once a message is displayed (rendered), it is removed from the session.

Controller Plugin

Yawik provides the controller plugin “Notification” (service name “notification”) to set notification messages in different namespaces.

It is merely a wrapper for Zend Framework’s FlashMessenger. It provides own namespaces and shortcut methods to add notifications according to Twitter Bootstrap alert class names.

The plugin is registered in the ControllerPluginManager under the key “Notification”

Namespace Class Constant Meaning
success Notification::NAMESPACE_SUCCESS An action was successfull
warning Notification::NAMESPACE_WARNING Action was (partly) successfull
danger Notification::NAMESPACE_DANGER Action was not successfull (error)
info Notification::NAMESPACE_INFO General info notification w/o special meaning

In a controller action, simply call the plugin via the magic __call mechanism

$this->notification()->success('Updates successfully changed.');
return $this->redirect(...);

The plugin provides following methods:

  • success($message)
  • warning($message)
  • danger($message)
  • error($message) [alias for danger(), for convinience]
  • info($message)
  • addMessage($message, $namespace = ‘info’)
  • __invoke($message = null, $namespace = ‘info’)
Rendering

To render notifications it is necessary to render the template which is registered under the key core/notifications in the view manager’s template map.

The default view script provided renders all notifications in a div container with the class “yk-notifications” using the “Alert” view helper.

Notifications are rendered in the following order:
  • Danger
  • Warning
  • Success
  • Info

You can place notifications into your general layout by following these steps:

  1. In the layout script, above the output of the headScript-Helper, render the notifications partial and capture to a variable. (Because the template injects a javascript to the headscript container)
  2. Echo the capture variable at the position where the notifications should be.
<?php $notifications = $this->partial('core/notifications'); ?>

//...

<?php echo $this->headScript(); ?>

// ...

<?php echo $notifications; ?>
Alert View Helper

The alert view helper takes a message and renders it in the bootstrap markup for an dismissable alert box. It is registered in the view helper manager under the key “Alert”.

<?php // capture content
$this->alert()->start('info'); ?>
<p>This is an info message</p>
<?php echo $this->alert()->end(); ?>

<?php // via __invoke
echo $this->alert('warning', 'This is a warning');

// via shortcut methods
echo $this->alert()->danger('This is an error message.');

The helper provides following methods

  • __invoke($type = null, $content = null)
  • start($type)
  • end()
  • info($content = true)
  • warning($content = true)
  • danger($content = true)
  • success($content = true)

Passing “true” (or nothing) to a shortcut method is the same as starting capture with the according type.

<?php $this->alert()->info() ?>
<p> This is an info message </p>
<?php echo $this->alert()->end() ?>

The resulting html will look something like this:

<div class="alert alert-info alert-dismissable">
    <button type="button" class="close" data-dismiss="alert">&times;</button>
    <p>This is an info message</p>
</div>

Logging

All PHP errors are logged into the log/tracy directory. This directory may contain the following files:

  • error.log: contains a list of exceptions, errors and notices
  • exception--<YYYY-MM-DD--HH-MM>--<hash>.html: contains a single HTML formatted exception trace
  • email-sent: is used for snoozing email notifications and usually contains ‘sent’ text

The path to this directory is configured in:

'tracy' => [
    'log' => __DIR__ . '/../../../log/tracy',
],

Headscripts

To inject script tags (with and without source) to the head section of the rendered output, YAWIK makes use of the Headscript view helper of Laminas Framework.

Inject scripts from a view script

To inject a script tag from a view script:

<!-- append a file -->
<?php $this->headscript()->appendFile($this->basePath('path/to/script.js')) ?>

<!-- prepend a file -->
<?php $this->headscript()->appendFile($this->basePath('path/to/script.js')) ?>

<!-- or use another method of Laminas helper. -->
Inject scripts via module.config.php

It is possible to inject head script tags using the module.config.php.

// inside module.config.php

return array(
    //...

    'view_helper_config' => array(
        // ...
        'headscript' => array(
            // append a script for all routes. (ommitting base path, it is added automatically)
            'path/to/script.js',

            // append a script for a special route name (or child routes)
            // note: you need to wrap script path in an array due to ZFs' config merging.
            'routename' => array('path/to/script.js'),

            // to prepend a script, you need to pass arguments to the headscript helper:
            'routename' => array(array(Headscript::FILE, 'path/to/script.js', 'PREPEND')),
        ),
    ),
    // ...

);

Note

The scripts from module.config.php are not included, if you use the default Headscript helper in your layout. You need to retrieve the ConfigHeadscript service from the view plugin manager, as its factory injects the scripts.

<?= $this->configHeadscript() ?>

Mails

Mails have two essential agents

  • a message-service, which is liable for gathering and providing data and rendering the mail
  • a mail-service, which is liable for sending the mail

when creating own mails, you usually extends the message

All related classes are in the Core-Modul, the interaction in in this diagram: http://www.gliffy.com/go/publish/7191865

Using the MailService

Mails can be used everywhere, where have access to the application-serviceLocator.

When you need to send a mail, there are four steps to do

  1. call the mail-service
  2. get a message-service from the mail-service (there are two distinguished types)
  3. feed the message-service with informations
  4. use the mail-service to send the message-service

The two types of message-service:

  • Templates, which uses scripts for the body and render them like usual views.
    This is more preferred approach for mails with lots of text, and also with mails for different languages
  • Derived classes, which is preferred when there is a lot of processing is involved.
Using a script as body

When using a script the message-service in some way behaves like a viewmodel, it takes in arbitrary variables, which can be accessed in the script. Also you can set a template, which is resolved by view-maps or view-pathes. In the scripts you can use PHP, and since the script is included into the message-service, you can set or change in the script mail-specific attributes like header oder subject. Scripts are an alike to views.

To use a script you have to instanciate a mail service and a htmltemplate service

$mailService             = $serviceManager->get('Core/MailService');
$mail                    = $mailService->get('htmltemplate');
$mail->entity            = $entity;
$mail->link              = $previewLink;
$mail->setTemplate('mail/myScript');
$mail->setSubject( /*translate*/ 'A Title');
$mail->setTo($email);
$mail->setFrom($userEmail, $userName);
$mailService->send($mail);

The script is set in the code, so there you can make the choice of the content, by simply choosing a script. But always remember to consign the location of the script in the template-map.

Note

The mail service injects itself in the view script in the variable “mail”, so you can access the mail service with $this->mail. But if you alter the headers (e.g. by setting a subject) you need to call the mail services renderBodyText() method prior to sending. Otherwise when using some transports (e.g. Smtp), the modifications made to the headers are NOT affecting the actual mail to be send.

This is caused by an internal implementation detail of the Zend Framework classes.

Using an own class

Own classes provide all information by methods. Own classes are the preferred choice when informations are volatile or special (like including pictures or other mimetypes). Look the classes in Applications\Mail for example. The own classes must be announced in the config like

'mails' => array(
    'invokables' => array(
        'myOwnClass' => 'xxx\Mail\myOwnClass',

With being announced, the mail-service can instantiate and initialize this class properly.

$mailService = $this->getServiceLocator()->get('Core/MailService');
$mail = $mailService->get('myOwnClass');
$mailService->send($mail);

Since most of the own classes are derived from laminas-mail (at least they should be derived from it), they will have a full pledge of all the methods, which are provided especially for mails, like setEncoding, setFrom etc…

Options

To modify the options, copy the module.core.options.local.php.dist to you config/autoload directory, remove the .dist prefix and adjust the values

Name type description
siteName string The siteName is used in Mails. Typically it’s the name of your website
operator array Contact Data, which can be used in Mail signatures or the imprint page
supportedLanguages array supported frontend languages a user can switch to
defaultLanguages string default language to use, if no language is set. Default “en”
detectLanguage bool if enabled, YAWIK tries to detect the language from browser settings (if no language is set in the users settings)
defaultCurrency string default currency to use, if no currency is set. Default “USD”
defaultTaxRate string default tax rate to use, if no tax rate is set. Default “19”

supportedLanguages is an assoziative array. The key is used for routing. The value is used as the locale. The upper case part of the locale defines the regions.

protected $supportedLanguages = array(
       'de' => 'de_DE',
       'fr' => 'fr',
       'en' => 'en_US',
       'es' => 'es',
       'it' => 'it',
       'el' => 'el_GR'
   );

provides core functionality

  • Sending Mails
  • Pagination
  • Error Handling
  • Configuration Handling
  • PDF Handling
  • Attachment handling
  • ACL for Attachments
  • general Layout

Layout

Note

the following table is generated automatically. Descriptions are marked from the view scripts files mit {{rtd: ...}}

Module Name Description
Core layout/layout General layout. Includes the HTML Header
Core error/404 File not found error page
Core error/403 Forbidden error page
Core error/index Internal Server Error Page (500)
Core main-navigation Renders a horizontal navigation with drop downs
Core pagination-control Renders paginations
Core core/loading-popup Renders a simple loading box while ajax requests are proceeded
Core core/notifications Renders default notification boxes.
Core form/core/buttons Renders default ‘save’ and ‘abort’ buttons
not found (form/core/privacy) WRONG CONFIGURATION  
Core core/form/permissions-fieldset Renders the group permission fieldset
Core core/form/permissions-collection Renders the group form
Core core/form/container-view Renders horizontal summary form
Auth form/auth/contact.form Renders the contact form within the application form and the personal profile.
Auth form/auth/contact.view renders the contact information within the application form and the personal profile.
Auth auth/form/user-info-container Renders horizontal form for the contact and the user photo
Auth auth/form/userselect Renders form for adding Users to a Group
Auth auth/form/social-profiles-fieldset Renders the fieldset for adding Social Profiles to an Application
Auth auth/form/social-profiles-button Renders the selection boxes for adding social profiles to an application
Auth auth/sidebar/groups-menu file exists
Applications applications/error/not-found Error Page for an Application form which references a non-existing job
not found (layout/apply) WRONG CONFIGURATION  
Applications applications/sidebar/manage currently not used
Applications applications/mail/forward Renders the email for forwarding an application
Applications applications/detail/pdf Renders a application as a simple HTML, used in the PDF generation and the forward mail
Applications applications/index/disclaimer Display the privacy policy disclaimer
Jobs jobs/sidebar/index Renders paginations
Jobs jobs/form/list-filter Renders the search formular for jobs used by recruiters
Jobs jobs/form/apply-identifier currently not used. Generates an reference number for jobs
Jobs jobs-publish-on-yawik displays short info about publishing on YAWIK
Jobs jobs-publish-on-jobsintown displays short info about publishing on Jobsintown.de
Jobs jobs-publish-on-homepage displays short info about publishing on the own homepage
Jobs jobs-terms-and-conditions display the terms and conditions, when publishing a job opening
Jobs mail/jobCreatedMail Mail is sent to the owner of yawik. The mail contains a link to an approval page, where the owner can accept or decline the job opening
Pdf pdf/application/details/button Renders the download as PDF Button, in the Applications Module
Geo geo/form/GeoText Renders the autocompletion for locations
Organizations organizations/index/edit Renders the formular for editing organizations
not found (piwik) WRONG CONFIGURATION  

Mail Templates

Module Name Description
Auth register sends a confirmation link to the user, after registration
Auth forgot-password sends a confirmation link to the user, after using the forgot password feature
Auth first-external-login sends the user login data, after the user wa created by an external application
Auth first-socialmedia-login sends the user a welcom mail after the first login via a social network

Services

Module Name Description
Core Core/Log Logging service
Auth HybridAuthAdapter Login via Social Networks
Auth AuthenticationService Authentication Service

Events

Name Description
core.create_paginator is fired, when CreatePaginator plugins creating a paginator
job.created is fired, when a user created a job opening.
job.accepted is fired, when an admin accepts a new or modifications on an existing job opening

Notifications

Every notification or message, no matter how it will be displayed or returned, runs through an unified API. This API is implemented in the Controller-Plugin ‘notification’. Notifications are session-persistent, that implies, they will pop up either on the current site, or on a following site. So unless you are sure of it, make no references to a current page, because the notification may pop up on a different page.

The common use is:

$this->notification('any text');
$this->notification()->success('any text');
$this->notification()->error('any text');
$this->notification()->info('any text');

To display notifications on a html-page, insert somewhere in the script or layout. In the standard-layout this partial is already included.

echo $this->partial('core/notifications');

If you have an ajax-request and expect back a JSON, the JSON-response should include information about notifications. You have to trigger an event with the whole response as data.

$.post(url, param, function(data) {
    $(this).trigger('ajax.ready', {'data': data});
})

Language Switcher

you can add a Language Switcher into you skin by:

<?=$this->languageSwitcher()?>

If you want to modify the Layout, edit the view script language-switcher.phtml

Auth


translation state of Auth module.

the auth module is based on hybridauth. The social networks Facebook, Xing and LinkedIn are ready to connect, just by configuring their API key and secret. Other Networks can be easily added.

User Data are stored in the users collection.

The Auth module offers the following features

  • Register with Facebook, Xing, LinkedIn, Google, Twitter or GitHub
  • Register via a configurable registration form
  • I forgot my Password
  • Roles for applicants, recruiters and admin
  • Notifications about new registrations

To configure the module, copy the auth.options.global.php into your config/autoload directory.

By using the optional Module CompanyRegistration, users can register as a company. The module provides a formular and creates a user and a company in one step.

Mails

template purpose triggered from
mail/register contains a confirmation-link to ensure the email-address. Without this assurance the account will not be fully activated  
mail/forgot-password Mail containing a link which enables the user to reset the password  
mail/first-socialmedia-login contains username and password. Mail is sent to the user after the first social media login  
mauk/first-external-login contains username and password. Mail is sent, after a user was created by an external application  

CV (Curriculum vitae)


translation state of CV module.

The CV module offers the possibility to store CVs. A CV consists of a contact, information about the preferred jobs and job location of a candidate and a collection of education histories, work experiences, personal skills and attachments.

The following workflow can be offered.

  • Job ist posted
  • Candidate applies
  • hiring organization must reject the applicant.
  • The applicant is asked, if he agree to be added to a talent pool.
  • If the applicant agrees, his application is copied to the CV module. He gets login data to the YAWIK installation
  • If the applicant disagrees, the application is deleted.

In addition thr following features will be offered

  • Recruiter can add multiple CVs.
  • Recuiter/Applicant can import CVs from Europass
  • Applicant can import CV from Social Network

Applications


translation state of Applications module

the application module offers an application formular, a list of applications and a detail view of an application. Depending on the users privileges the detail view offers a way to invite or reject an applicant, to rate an application or to forward an application by email.

If the PDF Module is installed, the Application can be downloaded as a PDF document with attachments and social profiles embedded.

Options

You can configure the possible mime-types or the maximum size of attachments by copying the applicationOptions into your config/autoload directory. Remove the “.dist” extention and adjust the values.

$options = array(
      /*
      * maximum size in bytes of an uploaded attachment, default 5MB
      */
      'attachmentsMaxSize' => '5000000',

      /*
      * allowed Mime-Type of an uploaded Attachment, default images, *.PDF, *.DOC, *.ODT
      */
      'attachmentsMimeType' => array('image','application/pdf', 'application/vnd.oasis.opendocument.text', 'application/msword'),


      /*
      * maximum amount of uploaded attachments
      */
      'attachmentsCount' => 5,

      /*
      * maximum size in bytes of an uploaded contact photo. default 500kB
      */
      'contactImageMaxSize' => '500000',

      /*
      * allowed Mime-Type of an uploaded contact photo
      */
      'contactImageMimeType' => array('image'),

      /*
      * allowed Mime-Types, images, plain text, *.PDF, *.DOC, *.ODT
      */
      'allowedMimeTypes' => array('image','text','application/pdf', 'application/vnd.oasis.opendocument.text', 'application/msword'),

  );

AbstractIdentifiableModificationDateAwareEntity

Events

you can attach Listeners to the following events

Name   description
EVENT_APPLICATION_POST_CREATE application.post.create Thrown, after an application was saved in the Database
EVENT_APPLICATION_PRE_DELETE application.pre.delete Thrown, befor an application is removed from the Database
EVENT_APPLICATION_STATUS_CHANGE application.status.change Thrown, befor an application is removed from the Database

API

It is possible to create an application through a POST request to api/apply passing in the apply id of the job ad as query parameter.

The data must be sent with the content type multipart/form-data

Field Value
Contact  
contact[gender]  
contact[first_name] First name
contact[last_name] Last name
contact[birthday] YYYY-mm-dd
contact[street]  
contact[house_number]  
contact[postal_code]  
contact[city]  
contact[country]  
contact[email]  
contact[image] user image (avatar) (must be an image)
General application data  
summary The cover letter
Facts  
facts[expected_salary]  
facts[earliest_starting_date]  
facts[driving_license] Possible values: 0, 1, yes, no
Attachments  
attachments[] One or multiple files

Every property of an application and its embedded documents can be send using the above mapping stategy. Field name ‘attachments[]’ sends a file as attachment for example.

The response is a json string. The complete application entity is returned.

# On success (HTTP-Code: 200)
{
    "status": "OK",
    "entity": {
        "resource_id": "Entity/Application",
        "job": "5c5abf660fc61f047c063b28",
        "user": "token:****************",
        "status": null,
        "contact": {
            "birth_day": null,
            "birth_month": null,
            "birth_year": null,
            "email": null,
            "is_email_verified": null,
            "gender": null,
            "first_name": "Firstname",
            "house_number": null,
            "last_name": null,
            "display_name": null,
            "phone": null,
            "postal_code": null,
            "city": null,
            "image": "/file/Applications.Attachment/user-image.png",
            "street": null,
            "country": null
        },
        "summary": null,
        "facts": {},
        "cv": {},
        "attachments": [
            "/file/Applications.Attachment/some-attachment.doc",
            "/file/Applications.Attachment/other-attachment.pdf"
        ],
        "profiles": {},
        "attributes": {},
        "id": null,
        "date_created": null,
        "date_modified": null
    }
}

# on Failure
# Either HTTP-Code 400 (No job for the apply id or invalid application data)
# or HTTP-Code 405 (Invalid request method)

{ "status": "Error", "message": "Meaningful error message" }
Examples

To try out the API it is best to use an application which is capable of sending post requests with file uploads, such as Postman.

_images/yawik-application-api-postman.png

Postman screenshot

Workflow

Organizations


translation state of Core module.

The organizations module adds a storage for organizations. If this module is enabled, a user can create an organization. He can invite emploees via email to his organization. Employees can have the following roles

Role Description
organization admin Owner of an organization
recruiter Default Role of an employee
department manager Department managers can accept or reject applications
manager currently unused

In addition the following permissions can be set

Permissions Description
create jobs User can create job postings
edit jobs User can view and edit job postings
view jobs User can view job postings
edit applications User can update applications. Eg. rate, invite, reject, etc
view applications User can view the application including attachments etc.

Applicants refer to Organization Names in their work history. Job Postings require an Organization Name. Either the name of the hiring Organization or the name of an agency. A Recruiter has to assign himself to an Organization.

Organization Names are just Names. They are public. Organization Names are assigned to various ratings. If an Organization Name is used as an hiring Organization for a job posting, or if a recruiter is using an Organization name for it’s own company, the ranking is modified.

An Organization entity itself only contains a reference to an organization Name.

CompanyRegistration

If you want to offer the registration for companies, this module might be helpful. It offers a registration form with additional fields. When a user registers, an user and an organization entity are created. This module requires the “Oganizations” Module.

Installation

to install the yawik/CompanyRegistration Module into a running YAWIK, change into the YAWIK/modules directory and clone the yawik/CompanyRegistration repository.

git clone https://github.com/yawik/CompanyRegistration

To activate the module create a php file named WhateverYouWant.module.php in your config autoload directory containing:

<?php
return ['CompanyRegistration'];

Another possibility to install YAWIK modules is using composer.

composer create-project cross-solution/yawik
cd yawik
composer require cross-solution/yawik-company-registration

This install the CompanyModule into the module directory of your YAWIK installation. You can uninstall the module via

composer remove cross-solution/yawik-company-registration

This removes the directory CompanyRegistration and all it content from your module directory of your YAWIK installation.

Configuration

The registration form contains by default the fields:

  • gender
  • name
  • email
  • organizationName
  • postalCode
  • city
  • street
  • houseNumber
  • phone

You can configure the registration form. Copy the RegistrationFormOptions.config.local.php.dist into your autoload directory and adjust the values.

Orders


translation state of orders module.

Requirements

a running YAWIK

Installation

to install the yawik/orders Module into a running YAWIK, change into the YAWIK/modules directory and clone the yawik/orders module.

git clone https://github.com/yawik/Orders

To activate the module create a php file named WhateverYouWant.module.php in your config autoload directory containing:

<?php
return ['Orders'];

Description

the orders module injects a billing address to to wizard for entering job postings. In addition it adds a storage for orders. By submitting a job posting, an order is created. The order contains all relevant data needed for billings. In addition, the module adds an invoice formular, which can be added into the order process. Default values of the invoice formular can be set in Settings/Orders.

Technically, the orders module offers the feature to take a snapshot of an entity.

Jobs


translation state of Jobs module.

The Jobs module allows to enter and manage job ads. In addition it generates a list of jobs. List of Jobs can be generated in a recruiter (Recruiter Mode) and a public search (Public Search Mode) mode

The entering process is defined at: http://www.gliffy.com/go/publish/6254781

_images/jobs_list-recruiter-mode.png

Recruiter Mode

in the recruiter mode the recruiter can see active and inactive jobs. In addition the list contains informations like number of applications (total/new) or the recruiters name, who is responsible for the position.

_images/jobs_list-search-mode.png

Public Search Mode

in search mode the users only see published jobs. This is normally used as a list of current vacancies, which is often used on a corporate website.

The list mode is defined by the users role.

It is also possible to configure YAWIK to run as a jobboard. There is a jobboard module which lets YAWIK act like a jobboard. This module is running on

http://jobs.yawik.org

Job Templates

you can create templates for entering job ads. All you need is an HTML version of your job opening. Simply replace the requirements, qualifications or benefits with a small piece of code. E.g.

<h4>Requirements:</h4>
<?php echo $this->requirements;?>

YAWIK replaces this code with an inline Wysiwyg HTML Editor if you want to modify your job opening. Otherwise the code is replaced by the HTML code, which was entered.

Modifications to the label fields labelBenefits, labelQualifications and labelRequirements are applied to all jobs of company, which are using the template.

You currently can use the following placeholders:

Name Description
$this->benefits Employee benefits
$this->city City of the company
$this->description description of the company
$this->descriptionEditable editable description of the company
$this->jobId ID of the job posting (since 0.29)
$this->qualifications Needed qualifications
$this->location Location of the job
$this->labelBenefits Label of the Benefits Section
$this->labelQualifications Label of the Qualifications Section
$this->labelRequirements Label of the Requirements Section
$this->oraganizationName Name of the company
$this->postalCode postalCode of the company
$this->requirements requirements of the job posting
$this->street Street of the company
$this->title editable title of the job posting
$this->headTitle title of the job posting
$this->uriApply URL a an application form
$this->uriJob URL a the job posting
$this->uriLogo URL of a company logo
$this->jobApplyButtons($this->applyData) Apply Button
$this->jobApplyButtons($this->applyData) Apply Button

Yawik comes with the example templates “default”, “modern” and “classic”. If you want to change the Templates within your Module, you can overwrite the template mapping adding the following configuration to your module config. Eg. you can put a file templates.config.php into your MyModule/config directory.

<?php
return [
   'view_manager' => [
       'template_map' => [
           'templates/default/index' => __DIR__ . '/../view/yourTemplate1/index.phtml',
           'templates/modern/index' => __DIR__ . '/../view/yourTemplate2/index.phtml',
           'templates/classic/index' => __DIR__ . '/../view/yourTemplate3/index.phtml',
       ]
   ]
];

If you want to modify the selection of the templates (iframe) add the following mapping

'iframe/iFrame.phtml' => __DIR__ . '/../view/YourTemplateSelection.phtml',

Mails

you can translate mails by adding the language to the template name. example: https://github.com/cross-solution/YAWIK/tree/develop/module/Auth/view/mail

Name Description
mail/job-created mail is sent to th approval team
mail/job-pending mail is sent to the person, who created the job.
mail/job-accepted mail informs the person, who created the job, that the job is going to be published
mail/job-rejected mail informs the person, who created the job, that the job was rejected

Options

To modify the options, copy the module.jobs.options.local.php.dist to you config/autoload directory, remove the .dist prefix and adjust the values

Name type description
multipostingApprovalMail string recipient email of the approval team
multipostingTargetUri string Send a Rest Request to this target on status changes of a job opening. The URI can contain username/password. eg: http://user:pass@host/location?query
defaultLogo string The default Logo is shown in a job opening and in the application form
companyLogoMaxSize int Maximum size in bytes of a company Logo. Default 200kB
companyLogoMimeType array Allowed Mime-Types for company Logos

Channel Options

The Channel Options contain information about publishing channels, a user can select to publish a job posting. To modify the options, copy the channel.options.local.php.dist to you config/autoload directory, remove the .dist prefix and adjust the values

Name type description
externalKey string external key of a channel. Eg. a provider offers the channel “MyJobboard” with the key “123”. YAWIK provides a channel “MyJobboard” using the key “myJobborad”. Set externalKey to “123”, if the job is published to the provider.
prices array [base,list,min] You can define 3 prices which you can use in your price-calculation
currency string currency of the price. Default: CoreOptions::defaultCurrency
tax int tax rate of the channel. Default: CoreOptions::defaultTaxRate
label string label of the channel
publishDuration int number of days a job opening can be published
category string Category of the channel. Default: “General”
headline string Headline of the channel
description string Description of the channel
linktext string Linktext of a link to further information of the channel
linkTarget string Link target of a link to further information of the channel
route string Route to a content page with details about the channel
params array Parameter, which can be used for linking the detail page about the channel

ATS Mode

The ATS (Applicant Tracking System) Mode defines, how applications should be processed. The following modes exist:

Name description
intern Applications are stored within the local YAWIK instance
uri Application Form is pointed to en external ATS System
email Application Form is forwarded via Email
none The Application Formular is deactivated

By using the ATS Mote intern, you can enable the One-Click-Apply Feature. This will add an additional Apply Button per selected social network into the job opening.

Widget

by using the folloging Javascript Widget you can add your jobs into your personal homepage.

<script>
   (function (window, document) {
       var loader = function () {
           var script = document.createElement("script"), tag = document.getElementsByTagName("script")[0];
           script.src = "view-source:https://yawik.org/YawikWidget/yawik.min.js";
           tag.parentNode.insertBefore(script, tag);
       };
       window.addEventListener ? window.addEventListener("load", loader, false) : window.attachEvent("onload", loader);
   })(window, document);
</script>

The javascript renders a joblist inside a container with the id YawikWidget

<div id="YawikWidget"
    data-organization="55ae775c6b10f8f05b8b457f"
    data-yawik="https://yawik.org/">
</div>

The attribute data-organizations takes an organization id, provided by your used yawik. The attribute data-yawik takes the location of the used yawik.

Source Code of the Widget: https://github.com/cbleek/YawikWidget

Price Calculation

The price calculations can be overridden by creating a MyCalculation.php. You can start by coping the ChannelPrices.php to MyCalculation.php. Adjust the namespace and implement your logic within the filter function.

To use your MyCalculation.php, you have to copy the ChannelPricesFactory.php into YourModule. Adjust the namespace and the $filterClass value.

To use your filter, you have to put the following config into your modules.config.php

'filters' => [
  'factories'=> [
     'Jobs/ChannelPrices'  => 'YourModule\Factory\Filter\MyCalculation',
     ...
    ]
 ]

One-Click-Apply

Since 0.25

You can simply add an apply button to you job opening by putting the following code into your job template.

<?=$this->jobApplyButtons($this->applyData)?>

This will use the ATS Mode settings and render the button. In addition to the ATS Mode settings you can set options to the Apply buttons. These options can be used to modify the layout and the behaviour of the Apply button.

<?=$this->jobApplyButtons(
    $this->applyData,
    [
        'sendImmediately' => true,
        'oneClickOnly' => false,
        'defaultLabel' => 'Click here to apply',
        'oneClickLabel' => null
    ]
)?>
name value description
sendImmediately bool true: Application is send immediately. Privacy policy are accepted by clicking on the button
oneClickOnly bool true: normal button, which refers to the form is hidden
defaultLabel string or NULL label of the normal button.
oneClickLabel string or NULL label of the OneClickApply Button

By modifying the labels, you normally loose the translations. Feature was sponsored by http://stellenmarkt.de

XML Feeds

Since 0.28 each job channel can be exported as an XML Feed. A lot of existing jobboards does not provide an API to publish jobs. Job publishing is often done via XML Feeds. Therefore a default XML Structure is provided, which can be imported by external jobboards. The XML Structure can be easily modified and my vary between different jobboards.

Example of the default XML:

https://yawik.org/demo/en/export/xml

PDF

the PDF modules enables to download an application as an PDF document

Geo

The Core module provides a form-field of the type “Location”. This form-field should be used whenever a Location is entered. In the Core the type “location” is an alias to a standard form field type “text”. So if the Geo module is inactive, a normal “text” field is used and nothing else happens.

But the Geo Module does a little bit more than just autocomplete the location. It uses a geo location service to enrich the entered data with geo coordinates and informations about the country and the region. If you enter “Frankfurt am Main”, the location will be defined as:

city = Frankfurt am Main
region = Hessen
Country = Germany
corrdinates = [8.6820934,50.1106529], type:"Point"

This makes it possible to use the distance feature, when searching e.g. for jobs. The Geo module currently ca use two different geo location services.

  1. the photon service
  2. the geo service

What’s the differences between those services.

Feature photon geo
multilingual yes no (only german is supported)
countries worldwide DE, AT, CH
search for postal codes no yes
Ranking nearest by population
needed requests 1 2
Ranking nearest by population
synchronized with OSM yes no
search for streets yes no
sources available yes no
free service available http://photon.yawik.org/api http://api.cross-solution.de

The Geo module can be easily configured to use one of the geo services by copying and modifying the Geo/config/Geo.options.local.php to the autoload directory of you YAWIK installation

<?php
/**
 * Name of the used geo coder plugin. You can use 'photon' or 'geo'. Photon is recommended.
 */
$plugin = 'photon';

/**
 * Location of your geo coder server. If unsure, leave it unchanged. Possible values are:
 * - http://photon.yawik.org/api
 * - http://api.cross-solution.de/geo
 */
$geoCoderUrl = 'http://photon.yawik.org/api';

//
// Do not change below this line!
//

return [
    'options' => [
        'Geo/Options' => [
            'options' => [
                'plugin' => $plugin,
                'geoCoderUrl' => $geoCoderUrl,
            ],
        ],
    ],
];

It it possible to configure

Solr

Requirements

current development is using:

  • php5-solr (pecl >= 2.4.0)
  • apache solr (6.6.5) (solr >7 is currently not working)

Note

Debian 8 ships with php5-solr 1.0.2. You can build your solr extension by:

aptitude install php5-dev libcurl4-openssl-dev libxml2-dev
pecl install solr
echo "extension=solr.so" > /etc/php5/mods-available/solr.ini
php5enmod solr
php -m| grep solr # should show the activated solr extension

Good resources on how to install solr:

Here is the way we’ve installed it in our Demo. First, you need JAVA. On Debian 8 you can install it via:

apt install -t jessie-backports  openjdk-8-jre-headless ca-certificates-java

then get a binary version of solr. The binary package contains an installation script. So unzip/untar it and execute the installation script. By default you’ll find your solr server in /opt/solr/. The solr data are stored in /var/solr/data/. After the installation you can remove the downloaded and extracted files.

> wget https://www-us.apache.org/dist/lucene/solr/6.6.5/solr-6.6.5.tgz
> tar xzf solr-6.6.5.tgz
> solr-6.6.5/bin/install_solr_service.sh solr-6.6.5.tgz

After the installation, solr server ist running at localhost port 8983. This is enough for yawik to be able the access the solr Server.

If you want to be able the access the solr frontend via https without touching the solr installation at all, an apache proxy may be a solution. If you want to use this solution, you have to enable the apache proxy module.

> a2enmod proxy proxy_http

For setting up an apache Proxy you can use a Virtual Host which looks like

<VirtualHost *:8443>

     ProxyRequests Off
     <Proxy *>
        AuthType Basic
        AuthName "Solr Search"
        AuthBasicProvider file
        AuthUserFile /etc/apache2/solr.passwd
        Require valid-user
        Order deny,allow
        Allow from all
     </Proxy>

     ProxyPass / http://localhost:8983/
     ProxyPassReverse / http://localhost:8983/

</VirtualHost>

Set the the user/pass in /etc/apache2/solr.passwd via htpasswd /etc/apache2/solr.passwd username

Installation

to install the yawik/solr Modul into a running YAWIK, change into the YAWIK/modules directory and clone the yawik/solr module .

git clone https://github.com/yawik/Solr

To activate the module create a php file named WhateverYouWant.module.php in your config autoload directory containing:

<?php
return ['Solr'];

To configure the solr connection copy the Solr options file into you autoload directory and adjust the values.

Available configuration options
Options
Option Type Description
secure bool Use SSL (https) [true] or not [false]
hostname string Hostname of the solr server
port int The TCP port. Default: 8983
username string Username used for HTTP Authentication (if needed)
password string Password of the HTTP Authentocation
jobsPath string The path to the solr jobs index
facetFields array List of facet fields. Each entry must be an array with at least the key “name”. An optional key “label” specifies a headline for the factes results.
facetLimit int Maps to facet.limit. Sets the maximum number of constraint counts that should be returned for the facet fields.
facetMinCount int Sets the minimum counts for facet fields that should be included in the response
parameterNames array Maps query parameter names to solr field names. (see .dist file for examples)
sorts array Specify the sort order used for an empty search. Must be an associated array where the key is the field name and the value is the sort order. (Default: [ “datePublishStart” => SolrQuery::ORDER_DESC ] )
filterQueries array todo
boostQueries array todo
cp module/Solr/config/solr.moduleoptions.local.php.dist config/autoload/solr.moduleoptions.local.php

Note

Solr needs a schema. The schema is currently a work in progress. You can use the schema in Solr/contrib.

Note

at least one field needs JTS. So if you want to use the contributed schema, you have to install JTS via:

mkdir tmp
cd tmp
wget wget https://downloads.sourceforge.net/project/jts-topo-suite/jts/1.14/jts-1.14.zip
unzip jts-1.14.zip
cp lib/*.jar /opt/solr-6.6.0/server/solr-webapp/webapp/WEB-INF/lib/

Or take a look at the issue https://github.com/yawik/Solr/issues/4 . Maybe the fiels using JTS is not needed by YAWIK any more.

If you want to set a user/password for solr you first have to enable an Authorization Plugin. Since Solr6 you can do so by copying the following json to /var/solr/data/security.json

This will add a user “solr” with the password “SolrRocks”. After that you can change the password with

curl --user solr:SolrRocks http://localhost:8983/solr/admin/authentication -H 'Content-type:application/json' \
    -d '{"set-user": {"solr" : "myPassword"}}'

you can initially index all active jobs by:

bin/console solr index job

Schema

fields  
id Primary key
applyId unique ID for the application
title Job title
city city of the job opening
dateCreated  
datePublishStart  
datePublishEnd  
dateModified  
lang language of the job opening
location location of the job posting (string)
organizationName name of the hiring Organziation
companyLogo logo of the hiring Organization
entityName possible values “job” or “location”
_MultiString Used by facets. E.g. region_MultiString, industry_MultiString, profession_MultiString

Description

YAWIK entities can be searched with the full text function of mongodb. This feature is great and usually sufficient, for example, to offer jobs on your own career site. If you want to use YAWIK as a job board, the requirements increase. A job exchange must be able to provide many jobs to many visitors. You need a search engine that scales. Currently Solr is supported.

With the solr module, the search is outsourced to the Solr search engine. The module synchronizes the search index with the jobs from the MongoDB.

Configuration

Since 0.9 you can use the following parameters to search

parameter  
l location
d distance
q query
organizationTag company
profession_Mutistring profession
industry_Multistring industry
employmentType_Multistring employmentType

YawikXingVendorApi

Requirements

You’ll need a Xing Account for publishing jobs. More infos: https://www.helpify.de/xing-posting-api-en/2937/how-can-i-as-a-developer-use-the-xing-posting-api

Installation

composer create-project cross-solution/yawik
cd yawik
composer require cross-solution/yawik-xing-vendor-api

This install the YawikXingVendorApi module into the module directory of your YAWIK installation. You can uninstall the module via

composer remove cross-solution/yawik-xing-vendor-api

This removes the directory YawikXingVendorApi and all it content from your module directory of your YAWIK installation.

Stackoverflow API

The StackoverfloApi module is able to push job openings to stackover. It implements https://talent.stackoverflow.com/api/doc

Settings

The settings module takes settings of other modules.

Eg. The order modules adds the possibility to configure an invoice address. This is simply done by defining a Settings Entity and a Settings Fieldset. The Settings module ensures that forms are rendered, values are stored and the navigation is extended by the corresponding sections.

Eg:

https://github.com/cross-solution/YAWIK/blob/develop/module/Orders/src/Entity/SettingsContainer.php

https://github.com/cross-solution/YAWIK/blob/develop/module/Orders/src/Form/InvoiceAddressSettingsFieldset.php

SimpleImport

The SimpleImport module offers a command line tool to import joboffers from feeds using a fix defined structure. It’s usefull, if you plan to use YAWIK as a jobportal and if you would like to import jobs from customers.

The CLI offers the following functionalisites.

Simple import operations

      yawik simpleimport import [--limit] [--name] [--id]    Executes a data import for all registered crawlers
      yawik simpleimport add-crawler                         Adds a new import crawler

      --limit=INT                 Number of crawlers to check per run. Default 3. 0 means no limit
      --name=STRING               The name of a crawler
      --id=STRING                 The Mongo object id of a crawler
      --organization==STRING      The ID of an organization
      --feed-uri=STRING           The URI pointing to a data to import
      --runDelay=INT              The number of minutes the next import run will be proceeded again
      --type=STRING               The type of an import (e.g. job)
      --jobInitialState=STRING    The initial state of an imported job
      --jobRecoverState=STRING    The state a job gets, if it was deleted, but found again in later runs.


      yawik simpleimport info    Displays a list of all available crawlers.
      yawik simpleimport info [--id] <name>    Shows information for a crawler
      yawik simpleimport update-crawler [--id] <name> [--rename] [--limit] [--organization] [--feed-uri] [--runDelay] [--type] [--jobInitalState] [--jobRecoverState]    Updates configuration for a crawler.
      yawik simpleimport delete-crawler [--id] <name>    Deletes an import crawler

      <name>             The name of the crawler to delete.
      --id               Treat <name> as the MongoID of the crawler
      --rename=STRING    Set a new name for the crawler.

      yawik simpleimport guess-language [--limit]    Find jobs without language set and pushes a guess-language job into the queue for each.

      --limit=INT    Maximum number of jobs to fetch. 0 means fetch all.


      yawik simpleimport check-classifications <root> <categories> [<query>]    Check job classifications.

      <root>          Root category ("industrues", "professions" or "employmentTypes")
      <categories>    Required categories, comma separated. E.g. "Fulltime, Internship"
      <query>         Search query for selecting jobs.


      --force    Do not ignore already checked jobs.

Feeds have to be formatted as defined in the scrapy docs.

Example: Add a feed to a an organization

You need an organizationId in order to add a crawler job. So at first you have to create a company in your yawik. The organizationId appears in an URL, if you try to edit the organization.

root@yawik:/var/www/YAWIK# ./vendor/bin/yawik simpleimport add-crawler --name=example-crawler \
                                                                  --organization=59e4b53e7bb2b553412f9be9 \
                                                                  --feed-uri=http://ftp.yawik.org/example.json
A new crawler with the ID "59e4b5a87bb2b567468b4567" has been successfully added.

This command created a crawler in the mongo collection simpleimport.crawler and returns the ObjectId.

> db.simpleimport.crawler.find({"_id":ObjectId("59e4b5a87bb2b567468b4567")}).pretty();
{
        "_id" : ObjectId("59e4b5a87bb2b567468b4567"),
        "name" : "example-crawler",
        "organization" : ObjectId("59e4b53e7bb2b553412f9be9"),
        "type" : "job",
        "feedUri" : "http://ftp.yawik.org/example.json",
        "runDelay" : NumberLong(1440),
        "dateLastRun" : {
                "date" : ISODate("1970-01-01T00:00:00Z"),
                "tz" : "+00:00"
        },
        "options" : {
                "initialState" : "active",
                "_doctrine_class_name" : "SimpleImport\\Entity\\JobOptions"
        }
}

Note

if you execute the command twice, the crawler will be added twice. If you want to remove a crawler, you have to do so on the mongo cli.

JobsByMail

The JobsByMail module offers a simple Formular to sign up to get the latest jobs by email. By activating the module you’ll be able to add the subscriber in your view form by adding the line <?=$this->proxy('jobsByMailSubscriptionForm')->render()?>

Features

  • view script for the subscriber form and a result page.
  • form ist pre-filled with the latest search parameters
  • form can be used as an authenticated and as an anonymous user
  • module works with or without the solr module

If the form is used by an anonymous users, a confirmation mail is send to the subscriber. Search profiles width confirmed email addresses will receive new jobs my mail.

The information Mail about new Jobs contains an unsubscribe Link.

Installation

to install the yawik/JobsByMail Modul into a running YAWIK, change into the YAWIK/modules directory and clone the yawik/solr module.

git clone https://github.com/yawik/JobsByMail

To activate the module create a php file named WhateverYouWant.module.php in your config autoload directory containing:

<?php
return ['JobsByMail'];

Usage

Customize

You can activate/deactivate Modules in config/config.php

$modules = array(
      'DoctrineModule',
      'DoctrineMongoODMModule',
      'Core',
      'Auth',
      'Cv',
      'Applications',
      'Jobs',
      'Settings',
      'Pdf',
 );

If you want to customize the layout, you can do so by writing a plugin. The easiest way is to clone the YawikDemoSkin into your modules directory.

cd modules
git clone https://github.com/cbleek/YawikDemoSkin

To activate the plugin you can either simply add 'YawikDemoSkin' to your modules array in config/config.php, or if you don’t want to touch any code from git at all, simply put a file named eg. config/autoload/MyModule.module.php in your autoload directory. Files named .module. are read to include additional Modules.

<?php
return array("YawikDemoSkin");

This will add the module dynamically.

If modules contain data like images, javasript or css, which should be directly accessable by the Webserver, these data should be placed into a directory named public. To make this directory accessible to the Webserver place a symbolic link into the YAWIK/public directory, pointing to the modules public directory.

cd YAWIK/public
ln -s ../modules/YawikDemoSkin/public YawikDemoSkin

It is a good practice to name the link with the modules name. This way, you can reference objects of the module by using the ModulesName within the URL.

Example: The YawikDemoSkin references its css in the layout.phtml

$this->headLink()->prependStylesheet($this->basePath() . '/YawikDemoSkin/YawikDemoSkin.css');

Next thing you propably want is to change the name of the Module. Search and replace all “YawikDemoSkin” with “MyModule” in the sources and rename the Directory “YawikDemoSkin” into “MyModule”. Do not forget to change the name in your “autoload/MyModule.module.php” file.

Now you have a module which you can use as a starting point for modifications.

customize your Skin by mapping more templates to your own views scripts.

If you want a completely own customized startpage, add a ‘startpage’ to your viewmap. It will be automatically picked, when you enter the name of the domain and have no session. But be aware, there is no login-box, unless you integrate it yourself.

CSS

YAWIK comes with bootstrap. Glyphicons are replaced by awesome fonts. The sources for for the main CSS is currently build with lessc. Bootstrap and awesome font sources are symlinked to the vendor directory`. The global CSS file is build with make-css.sh

you can install lessc on ubuntu by

sudo apt-get install npm
sudo npm install -g less

Our YawikDemoSkin can be seen as an example, how to modify the CSS. The Skin needs a different height for the fixed footer. This is achieved by creating a new less file, which can import our yawik/yawik.less (a symlink is pointing to it). You can overwrite all less variables.

@import "yawik/yawik.less";
@footer-height:                     39px;

Your customized CSS can be compiled with lessc like:

lessc YawikDemoSkin.less ../public/YawikDemoSkin.css

Formular Fields

Name description
Editor Editor element
FileUpload FileUpload Form element
InfoCheckbox InfoCheckbox Form element. Adds a Link like to the description Text.
Location autocomplete a location and adds additional Geo data, see: Once Click Apply
Phone adds Validation for a phone number
Rating Star rating Element
SpinnerSubmit a spinner icon is added during form validation. While sending data, the submit button is inactivated

View Helper Scripts

Name description
Alert displays notification like error or success
Services can access Services within view view scripts
jobUrl displays the link to a job posting.
applyUrl displays the link to an application form of a job posting.
applyButton displays application buttons. see: Geo Module
languageSwitcher renders a language switcher select box. see: Language Switcher

Guidelines

Common JavaScript Trigger

Trigger are used to broadcast certain events

ajax.ready
Is triggered on the container, which is altered by the ajax.request. Remember, this event bubbles, so all listener on elements above will spring into action, too.

Naming Conventions

We are following the Zend Framework Coding Standard for PHP

  • Variables: lowerCaseStartingCamelCase like ….
  • Modules and Classes: UpperCaseStartingCamelCase
  • Array keys (options arrays): underscore_separated ( ‘option_key’ )
  • Service names Module/[SubCategory/]Service ok.
  • Configuration Keys invocable form element: UpperCaseStartingCamelCase, <Module>/<Element> like ‘Applications/Mail’
  • Configuration Keys invocable controllers: UpperCaseStartingCamelCase, <Module>/<Element> like ‘Applications/Mail’
  • Configuration Keys view scripts: lowercase, dash-separated, like ‘applications/index/disclaimer’

Behat

Behat tests are executing scenarios within a browser. Do do so, you need a running YAWIK Installation, the virtual framebuffer xvfb and the selenium.

The framebuffer can be installed via apt-get install xvfb. Once installed it can be started by

/sbin/start-stop-daemon --start --quiet --pidfile /tmp/xvfb_99.pid --make-pidfile --background
    --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1680x1050x16

The “browser” is started via.

java -jar /home/cbleek/Projects/YAWIK/vendor/se/selenium-server-standalone/bin/selenium-server-standalone.jar \
      -Dwebdriver.chrome.driver=/usr/chromedriver

The tests itself are started.

APPLICATION_ENV=development ./vendor/bin/behat --strict \
    --no-interaction -vvv -f progress  --tags="@javascript && ~@todo && ~@skip-travis"

API

YAWIK currently offers a simple API to create Jobs. If you are able to create HTML formatted job ads and if you can put application links to the HTML source of your jobs, you might find the API usefull to put your jobs into YAWIK, which enables you the use the application forms.

We’ll later replace this simple API with a full featured. Maybe build with https://apigility.org/.

YAWIK allows an external application to post jobs via http POST requests. At first you have to authenticate. This can be done by:

Example:

curl -c "/tmp/cookie" -d "appKey=SecretYawikDemoKey&user=demo&pass=demo" http://yawik.org/demo/login/extern?format=json

The following parameters can be passed:

Param Value Description
appKey string pre-shared key between your app and your YAWIK
user string user name of an YAWIK Account
pass string password of an YAWIK Account
email email adress If an YAWIK Account is created, the password is sent to this email address
role recruiter or user If an YAWIK Account is created, this role is used

The appKey authenticates your external application. The to “top secret” pre-shared key of the YAWIK demo is SecretYawikDemoKey. Normally this key is only known by YAWIK and the external App.

Success:

{"status":"success","token":"em7ke40ec5sskqce5jgtt912m5"}

Failure:

{"status":"failure","code":0,"messages":["Invalid application key"]}

Once you’ve logged in, the cookie returned by YAWIK has to be stored on the external application site. In case you’re testing it with the above curl statement, the cookie is stored in /tmp/cookie.

If the application Key is valid and the user is unknown, a user is simply created. The password of the user is sent via email (if an email-value is provided in the curl-call). Authentification via CURL and the normal login are traded different. Every authentification with an external application uses a separate key, therefore the external application is liable to provide privacy. An user can root out any external application by simply revoking it’s key without affecting any other authentification.

These parameters are available or must be set to transmit a job:

Param Value example mandatory Description
applyId string AMS79j yes the id is an unique key to adress your job
company string   yes name of the company
companyId string   no if an id is provided, the company is stored in the YAWIK-DB
contactEmail email adress   no for automatic informations like new applicants
title string   yes for tabular overview
location string   no for overview, will be later prone for indexing
link http adress   yes the job offer weblink
datePublishStart YYYY-MM-DD   yes  
status string   no Possible values
reference string   no an internal reference from the publisher
logoRef http adress   no Logo for the Company
uriPublisher http adress   no who get the credit for any application
atsEnabled boolean   no set to true shows up a link to an application form
uriApply http adress   no adress to an external application form

some remarks:

applyId
The applyId must be unique just to the provider, this key along with your authentification is the only access to your data. Consistently there is no key provided by YAWIK.
atsEnabled
enables the Applicant Tracking System for the job opening.
company, companyId
companies can managed alongside the job if a companyId is passed, the companyId is an assurance for yawik, that different jobs with the same companyId belong to the same company. The name is for that a to weak criteria.
contactEmail
Although a contact-email is not obligatory, it is a crucial enhancement of service. Whenever something happens to your job, you get an update. This includes new applicants for a job.
link
This link should be an appealing presentation of the job. YAWIK can not (up to now) display Jobs on it own, so this link is mandatory.
uriPublisher
One of the basic ideas of YAWIK is to distribute jobs automatically. Even though, every job may have an owner who wants to administer the job. The Adress of uriPublisher must provide and own rest-service for updates or feedback of informations.
uriApply
As joboffers can be distributed, the application could directly linked back to the source. The distributing system can add a signature to the application to indicate where the applicant has first seen the job offer.

Current States of job openings are defined in Jobs/Entity/StatusInterface

Status Description
CREATED job opening was created
WAITING_FOR_APPROVAL entering of a job opening was finished
REJECTED the job was rejected
PUBLISH job was accepted an is going to be published
ACTIVE the job is online
INACTIVE job should go offline
EXPIRED the job is expired
curl -b /tmp/cookie -d "applyId=1234" 'http://yawik.org/demo/de/saveJob?format=json'
{
    "token":"903rgbrs1j6p5gb2586tdci833",
    "isSaved":false,
    "post":{"applyId":"1234"},
    "valid Error":
        {
        "job":
            {
                "company":{"isEmpty":"Es wird ein Eingabewert ben\u00f6tigt. Dieser darf nicht leer sein"},
                "title":{"isEmpty":"Es wird ein Eingabewert ben\u00f6tigt. Dieser darf nicht leer sein"},
                "link":{"isEmpty":"Es wird ein Eingabewert ben\u00f6tigt. Dieser darf nicht leer sein"},
                "datePublishStart":{"isEmpty":"Es wird ein Eingabewert ben\u00f6tigt. Dieser darf nicht leer sein"
            }
        }
    }
}

A successfull request returns:

curl -b /tmp/cookie -d "applyId=1234&title=this%20is%20a%20test%20job&company=MyCompany&datePublishStart=2014-09-15&link=http://example.com/myjob.html" \
       'http://yawik.org/demo/de/saveJob?format=json'
{
    "token":"903rgbrs1j6p5gb2586tdci833",
    "isSaved":true,
    "post":{
        "applyId":"1234",
        "title":"this is a test job",
        "company":"MyCompany",
        "datePublishStart":"2014-09-15",
        "link":"http:\/\/example.com\/myjob.html"
    }
}

Transfering Jobs

Jobs are transferred as complete JSON-Objects, there are no RESTful API HTTP methods like get, put, post, delete. That’s why this is considered a pure

curl name:password@server/report/job
    -H 'Accept: application/json'
    -H 'Content-Type: application/json'
    -d '{
        "applyId": "ref_from_yawik_123",
        "title": "lorem ipsum title",
        "description": "lorem ipsum body",
        "company": "tcomp",
        "contactEmail": "weitz@cross-solution",
        "location": "35510 Butzbach",
        "link": ".",
        "datePublishStart": "2013-08-20T08:19:12.000Z",
        "status": "active"
    }'
applyId
is a textfield is always the ID of the sending system, it is within the duty of the receiving system to classify the ID to the system, it is recommended to use information of the send-header, like referer-host. The apply-id is mandatory.
title
is a textfield title is for a fast human readable classification. The title is mandatory.
description
is (propably) a textfield is all information displayed on the target. The content is not specified on purpose, so it’s up to the user how to define the description. Though it is recommended to use HTML or at least XML. The description is optional, especially if you use an external link to the job offer.
company
is a textfield a company-name is mandatory, for just the reason it is mandatory in every job-offer in the system. It should provide an allocation for accounting
contactEmail
is a textfield in case of question or feedback although it is not settled, if this concerns just the content of job or the provider of jobs, this is mandatory
location
is a textfield about the location of the job-offer, Since a lot of jobs have no specific location, this is optional.
link
is a textfield, when the job-offer should redirect or link to an external page. This is always recommended for high glossy jobs.offers. But - buyers aware - you have to be cognizant that external linking can be disabled for some reason. Anyway, this is optional.
datePublishStart
is a textfield in the format YYYY-MM-DDTHH:II:SS.FRACZ (Uni-Format), It can be easely interpreted with PHP and is the time-format in MONGO. This field is just to facilitate the process in transferring jobs in advance. Nonetheless it is a matter of personal agreements for ending a job-offer. As we can not ensure a notice of a premature ending of a job-offer this is not stringent. Therefore this is optional.
status
is a textfield this is for putting a job on passiv by external command, or switch it back to active. Default is always active. This field is optional.

Frequently Asked Questions

FAQ: Mails

Is it possible to configure an SMTP Server

Yes. Copy the Core/config/MailServiceOptions.config.local.php.dist into you autoload directory and adjects the values.

How can I translate Mails?

translating mails with gettext is possible, but you will propably like to translate mails a little different. You can code the language into the name of the view script.

example:

your’re creating a mail using the viewscript MyModule/view/myMail/mailtext.phtml. If you wannt to translate this mail into the french lanuage, you simple copy it to MyModule/view/myMail/mailtext.fr.phtml.

FAQ: Navigation

How can I remove Mait templates from the settings menu?

Modules can implement SettingEntities. If they do so, they will be automatically inserted into the navigation. If you want to disable this feature, you can unset Modules Settings in your configuration. Place the following configuration into your autoload/my-navigation.local.php

<?php
return [
    'Applications' => [
        'settings' => null,
    ],
    'Core' => [
        'settings' => null,
    ]
];

How can I hide the navigation on the application form?

In the YawikDemoSkin Module, you can see how this can be done.

https://github.com/cbleek/YawikDemoSkin/blob/master/Module.php#L65

FAQ: Translation

How can I translate my module?

we use gettext for translation as default. Gettext by default scans the sources for translate() and setLabel() function calls. In addition, we’ve defined, that strings following the annotation /*@translate*/ should be translated as well. This is done by the little script translate. It scans .php and .phtml files for /*@translate*/ annotations and puts all following strings into the module/MyModule/language/_annotated_trings.php file.

Note

This mechanism has the limitation, that the string which follows the annotation must be in one line.

Example

/*@translate*/ 'this will be found by gettext'

/*@translate*/
'this will not be found by gettext'

executing bin/translate module/MyModule will scan all .php and .phtml files for texts to translate. This will create .po file in the module/MyModule/language directory, which you may translate with poedit

User Feedback:

  • “bin/translate module/MyModule” ausführen
  • poedit (unter kubuntu, Version 1.5.4, deutsche Version) starten
  • die de_DE.po öffnen
  • im Menü ‘Katalog’ -> ‘Aus POT-Datei aktualisieren …’ wählen
  • ‘messages.pot’ wählen

Nach Änderungen dann die de_DE.po speichern und das translate - Skript nochmal ausführen.

FAQ: General

File Upload does not work?

when trying to upload a file, the status wheel turns forever. Uploaded file is not stored.

This happens, if a javascript error occurse. You can only debug such a problem by using firebug or comparable developer tools. The MimeType of uploaded files is checked by default using the libmagic. Please make sure that:

  • the fileinfo extention exists. On FreeBSD, this extension has to be installed. On Linux, this extention is normally included by default. Check it by: php -m | grep fileinfo
  • Make sure, your Webserver can access /usr/share/misc/magic*. These files are referenced by YAWIK/vendor/zendframework/zend-validator/src/File/MimeType.php
  • make sure the access is not restricted by an open_basedir setting

File Upload shows “An unknown error occured” on large files

When the upload seems to work, but at the end, it shows an “An unknown error occured”, and in the log/error.log appears a line like

“ERR POST Content-Length of 16414047 bytes exceeds the limit of 8388608 bytes (errno 2)”

you should check that you set all required configuration values.

  • The allowed max size must be set in the yawik configurations .e.g. for attachments in an application the option ‘attachmentsMaxSize’ in the file config/autoload/applications.forms.global.php must be set appropriatly.
  • The php.ini value of ‘upload_max_size’ must also be set accordingly. Either in the php.ini or (for apache) via ‘php_admin_value’
  • Do not forget the ‘post_max_size’ php.ini option.

FAQ: Documentation

How can I improve the documentation?

The recommended way is to send us pull requests. If you don’t know, how to do this, send us documentation via mail to contact@yawik.org.

FAQ: XML Feeds

Can I export jobs via xml feeds?

Yes. YAWIK offers since 0.28 a default xml feed for all public job openings via the route lang/export/xml. Means for our demo https://yawik.org/demo/en/export/xml.

If you want do modify the xml structure to fit you needs, override the view jobs/export/feed. The view script gets injected a paginator containing jobs as jobs.

If you need different XML formats for different channels (in case you are offering multiposting) you can access feeds for different channels by extending the route name with the channel name. Example: our demo offers a channel yawik. The feed for this channel can be accessed via https://yawik.org/demo/en/export/xml . If YAWIK finds a view script named feed.yawik.xml.phtml, it uses it to render the xml. Otherwise it uses the default structure defined by feed.xml.phtml. You can access a channel feed via: https://yawik.org/demo/en/export/xml/yawik

Channel feeds only contain jobs, which are public on a certain feed. The URLs to job openings and application forms are containing the channel name, which makes counting easy.

If you use the YawikSolr module, Solr results are injected into the feeds automatically. Data, which are not available in solr are lazy loaded from mongo.

FAQ: Customize Formulars

Is it possible to use a certain list of locations?

Yes. Since 0.29 it is possible to customize forms.

The Geo module offers to Form Elements. LocationSelect, which creates an autocomplete search fields for a location and SimpleLocationSelect, which can be used to create a select field with a certain list of locations.

Example

Company XY has branch offices in Frankfurt, München and Stuttgart. The HR People want to simply select one of the location, if a job posting is created. This can be done by creating a config file jobs.baseform.global.php in the the config/autoload directory.

<?php

return [
    'options' => [
        'Jobs/BaseFieldsetOptions' => [
            'options' => [
                'fields' => [
                    'geoLocation' => [
                        'type' => 'SimpleLocationSelect',
                        'options' => [
                            'value_options' => [
                                '{"city":"Stuttgart","region":"Baden-Wuerttemberg","coordinates":{"type":"Point","coordinates":[9.17702,48.78232]}}' => 'Stuttgart',
                                '{"city":"Frankfurt","region":"Hessen","coordinates":{"type":"Point","coordinates":[8.68212,50.11092]}}' => 'Frankfurt',
                            ],
                        ],
                        'attributes' => [
                            'data-searchbox' => '-1',
                            'data-placeholder' => 'please select',
                            'data-allowclear' => 'true',
                        ],
                    ],
                ],
            ],
        ],
    ],
];
_images/SimpleLocationSelectElement.png

When using SimpleLocationSelect, the form element comes with a search element. By entering a location, matching locations are fetched from a geo service.

_images/LocationSelectElement.png

When using LocationSelect the form looks like. There is a fixed list of locations. You can enrich your locations with all attributes of a Location entity

_images/LocationElementView.png

Indices and tables