Авторизация учетными данными JIRA в PHP

В этом конкретном случае «прикручено» в Yii. Читаем непосредственно с таблицы пользователей Jira
<?php

namespace app\models;

class User extends \yii\base\Object implements \yii\web\IdentityInterface
{
    public $id;
    public $username;
    public $password;
    public $authKey;
    public $accessToken;

    public $credential;
    public $first_name;
    public $last_name;
    public $gid;
    public $group_name;


    public static function _init() {
	if (empty(self::$users)) {
		$users = \Yii::$app->db->createCommand("select c.id, c.lower_user_name username, c.credential, c.first_name, c.last_name from cwd_user c where c.active = 1")
           			->cache(3600)
           			->queryAll();
           	foreach ($users as $u) {
			self::$users[$u['id']] = $u;
		}
	}

    }

    public static $users = [];
...............................
    /**
     * Validates password
     *
     * @param  string  $password password to validate
     * @return boolean if password provided is valid for current user
     */
    public function validatePassword($password)
    {
	return JiraAuth::isValidPassword($password, $this->credential);
    }
} 

И сам класс проверки пароля
<?php

class JiraAuth {
	const DEFAULT_PREFIX = '{PKCS5S2}';
	const DEFAULT_SALT_LENGTH_BYTES = 16;

	public static function isValidPassword($password, $credential) {
		$ret = false;

		if ( strrpos($credential, JiraAuth::DEFAULT_PREFIX) === false)
			return $ret;

		$credential = substr($credential, strlen(JiraAuth::DEFAULT_PREFIX)-1);

		$binSaltAndKey = base64_decode($credential);
		
		if (strlen($binSaltAndKey)<48)
			return $ret;

		$salt = substr($binSaltAndKey,0,JiraAuth::DEFAULT_SALT_LENGTH_BYTES);
		$oldKey = bin2hex(substr($binSaltAndKey,JiraAuth::DEFAULT_SALT_LENGTH_BYTES));
		$newKey = hash_pbkdf2('sha1', $password, $salt, 10000, 64);

		return ($oldKey == $newKey);
	}
}
Замечание, PHP нужен от 5.5, а JIRA была версии v6.3

Загрузка файлов, параметр filename в HTTP заголовке Content-Disposition

«Крякозябры» вместо имени файла в IE или ошибка загрузки и сообщение о дублирующемся заголовке на движке WebKit.
if (file_exists($path . '/' . $dfi->hashedName)) {
	$response = new \Http\Response();
	$response->setHeader("X-UA-Compatible", 'IE=edge');
	$response->setHeader("Content-Type", isset($dfi->mimeType) && !$nomime  ? $dfi->mimeType : 'application/octet-stream');
	$response->setHeader("Content-Transfer-Encoding", 'binary');
	$response->setHeader("Content-Disposition", ($nomime ? 'attachment' : 'inline') . '; filename=' . str_replace(['\\','"','#','*',';',':','|','<','>','/','?',','], '_', mb_convert_encoding($dfi->originalName, 'CP1251', 'UTF-8')) . '; filename*=UTF-8\'\''.rawurlencode($dfi->originalName).'');

	$response->setHeader("Content-Length", (string)(filesize($fpath)));
	$response->sendHeaders();
	if ($file = fopen($fpath, 'rb')) {
		while(!feof($file) and (connection_status()==0 || 1)) {
			print(fread($file, 1024*8));
			flush();
		}
		fclose($file);
	}
	exit;
}

Определяем инициатора события (jQuery)

Определить программно вызвано событие $('#elem').trigger('clik') или пользователь «кликнул» по элементу. Варианты:
$( '#elem' ).on('click', function ( e ) {
    if ( e.originalEvent === undefined ) {
        alert( 'triggered programmatically' );   
    } else {
        alert( 'clicked by the user' );   
    }
})

или
$( '#elem' ).on('click', function ( e ) {
    if ( e.isTrigger !== undefined ) {
        alert( 'triggered programmatically' );   
    } else {
        alert( 'clicked by the user' );   
    }
})

или
$( '.elems' ).on('click', function ( e, a ) {
    if ( a !== undefined ) {
        alert( 'triggered programmatically ' + a.from );   
    } else {
        alert( 'clicked by the user' );   
    }
})
$( '#elem' ).trigger('click',{'from':'test'});

Использование переменной окружения .htaccess

cat ./.htaccess
SetEnv APPLICATION_ENV production
AddDefaultCharset UTF-8
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
Используем в php (по умолчанию будет использоваться development):
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV',
              (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
                                         : 'development'));
Вся затея ради того, чтобы использую настройки окружения хоста переключаться между БД разработки и продакшн
public $db_our = '[Metrolog' . (APPLICATION_ENV!='production' ? 'Dev' : '') . '].[dbo].';
И сразу, защитим .htaccess от нечаянного изменений даже в случае наличия рутовых прав, выставим атрибут файлу IMMUTABLE(Неизменяемый) (это для Linux)
# lsattr ./.htaccess
-------------e-- ./.htaccess
# chattr +i ./.htaccess
# lsattr ./.htaccess
----i--------e-- ./.htaccess
(a это для UNIX)
# ls -lo ./.htaccess
-rw-r--r--  1 rianvi  wheel  - 375 Feb 11  2013 ./.htaccess
# chflags schg ./.htaccess
# ls -lo ./.htaccess
-rw-r--r--  1 rianvi  wheel  schg 375 Feb 11  2013 ./.htaccess

Вставка изображения в форму из буфера

Возникла необходимость, вернее, усталость от неудобства. В багтрекер постоянно нужно вставлять скриншоты, и вот как раз сохранение в файл перед прикреплением в форму и достало. Кроссбраузерностью и поддержкой старых версий не заморачивался, в компании повсеместно ИЕ, обновления приходят и устанавливаются постоянно. Сам я предпочитаю Хром. В итоге так и получилось, ИЕ > 9, Хром, ну и Опера, так как она сейчас на ВебКите, Лисицу чинить не стал.
Безымянный51 Безымянный52
В форму добавляю:
<input type="hidden" id="screenshot" name="screenshot" value="">
<div style="margin-top: 20px; position: relative;"> <img id="isimg" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII="></img> <div id="isforimg" contenteditable="true"> </div> </div>
Стили:
<style>
#isimg {
margin-left: 100px;
padding: 5px;
width: 320px;
height: 220px;
border: none;
background: url(../images/screenshot-grey.png) no-repeat center center;
}
#isforimg {
position: absolute;
padding: 5px;
width: 70px;
height: 70px;
overflow: hidden;
border: 2px solid #eee;
top: -10px;
left: 370px;
text-align: center;
vertical-align: middle;
line-height: 60px;
background: url(../images/klipper.png) no-repeat center center;
}
</style>

На сервере:
if (!empty($_POST['screenshot']) && ($content_pos = strpos($_POST['screenshot'], ';base64,')) && $content_pos!==false) {
try { $filename = md5($issue->getIssueId().'screenshot'.microtime(1)); $file_path = $folderPath.DIRECTORY_SEPARATOR.$filename; $fcontent = base64_decode(substr($_POST['screenshot'], $content_pos + 8)); if (false !== file_put_contents($file_path, $fcontent)) {

И js (без jQuery):

if(document.attachEvent) {
document.attachEvent('paste', onPasteTriggered); // ie < 11
}
else if(document.addEventListener) {
document.addEventListener('paste', onPasteTriggered, false); // google chrome or ie >= 11
}

function onPasteTriggered(e) {

if (e.srcElement.id != 'isforimg')
return;

if (e.clipboardData || e.view.clipboardData) { //google chrome or ie > 9
var copiedData = e.clipboardData ? e.clipboardData.items[0] : e.view.clipboardData.files[0]; //Get the clipboard data

/*if the clipboard data is of type image, read the data*/
if(copiedData && copiedData.type && copiedData.type.indexOf('image') == 0) {

var imageFile = e.clipboardData ? copiedData.getAsFile() : copiedData;

/*HTML5 FileReader API to read the image file*/
var reader = new FileReader();

reader.onload = function (evt) {
var result = evt.target.result; //base64 encoded image
document.getElementById('screenshot').value = result;
document.getElementById('isimg').src = result;
};
reader.readAsDataURL(imageFile);

document.getElementById('isforimg').setAttribute('contenteditable', 'false'); // for IE - for not insert img

setTimeout(function(){document.getElementById('isforimg').setAttribute('contenteditable', 'true');}, 200); //
}
}

setTimeout(function(){ document.getElementById('isforimg').innerHTML = ''; }, 100); //erase what inserted
}