How to customize the MediaWiki VisualEditor - mediawiki

I have the MediaWiki VisualEditor extension running on my Wiki and it works great. However, I'd like to customize the main VE toolbar to add a list of different font styles such as:
1. new --> would wrap the text in <span class='new'>text here</span>
2. old --> would wrap the text in <span class='old'>text here</span>
Is there an example on how to accomplish this?
The answer under the link question may be going in the right direction, but I couldn't get it to work in my environment - the link wouldn't get added to my VE toolbar. Maybe I'm missing a step, but even then, it would only add a link, and not wrap a style around my text. So please help!

So I finally solved this problem myself after lots of trial and error and looking at many different posts; but I'm not confident that this isn't a hack; in fact, I'm editing the existing classes - adding my own annotations - instead of creating a clean extension.
Here is the VisualEditor version that I've modified:
VisualEditor: REL1_25
2015-09-17T18:15:26
be84313
Here are the diffs of my modifications:
diff -r VisualEditor/extension.json /var/www/html/wiki/extensions/VisualEditor/extension.json
535a536,537
> "lib/ve/src/dm/annotations/ve.dm.StrongAnnotation.js",
> "lib/ve/src/dm/annotations/ve.dm.InsAnnotation.js",
602a605,606
> "lib/ve/src/ce/annotations/ve.ce.StrongAnnotation.js",
> "lib/ve/src/ce/annotations/ve.ce.InsAnnotation.js",
755a760,762
> "visualeditor-annotationbutton-strong-tooltip",
> "visualeditor-annotationbutton-ins-tooltip",
> "visualeditor-annotationbutton-highlight-tooltip",
diff -r VisualEditor/lib/ve/i18n/en.json /var/www/html/wiki/extensions/VisualEditor/lib/ve/i18n/en.json
30c30
< "visualeditor-annotationbutton-strikethrough-tooltip": "Strikethrough",
---
> "visualeditor-annotationbutton-strikethrough-tooltip": "Deleted",
33a34,36
> "visualeditor-annotationbutton-strong-tooltip": "Hot",
> "visualeditor-annotationbutton-ins-tooltip": "New edit",
> "visualeditor-annotationbutton-highlight-tooltip": "Marked outdated",
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/lib/oojs-ui/themes/apex/images/icons: new.svg
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/lib/oojs-ui/themes/mediawiki/images/icons: new.svg
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ce/annotations: ve.ce.InsAnnotation.js
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ce/annotations: ve.ce.StrongAnnotation.js
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/dm/annotations: ve.dm.InsAnnotation.js
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/dm/annotations: ve.dm.StrongAnnotation.js
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/styles/images/icons: hot.svg
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/styles/images/icons: new.svg
Only in /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/styles/images/icons: old.svg
diff -r VisualEditor/lib/ve/src/ui/styles/ve.ui.Icons.css /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/styles/ve.ui.Icons.css
198a199,210
> .oo-ui-icon-strong {
> /* #embed */
> background-image: url(images/icons/hot.svg); }
>
> .oo-ui-icon-new {
> /* #embed */
> background-image: url(images/icons/new.svg); }
>
> .oo-ui-icon-highlight {
> /* #embed */
> background-image: url(images/icons/old.svg); }
>
diff -r VisualEditor/lib/ve/src/ui/tools/ve.ui.AnnotationTool.js /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/tools/ve.ui.AnnotationTool.js
180a181,246
> * UserInterface strong tool.
> *
> * #class
> * #extends ve.ui.AnnotationTool
> * #constructor
> * #param {OO.ui.ToolGroup} toolGroup
> * #param {Object} [config] Configuration options
> */
> ve.ui.StrongAnnotationTool = function VeUiStrongAnnotationTool( toolGroup, config ) {
> ve.ui.AnnotationTool.call( this, toolGroup, config );
> };
> OO.inheritClass( ve.ui.StrongAnnotationTool, ve.ui.AnnotationTool );
> ve.ui.StrongAnnotationTool.static.name = 'strong';
> ve.ui.StrongAnnotationTool.static.group = 'textStyle';
> ve.ui.StrongAnnotationTool.static.icon = 'strong';
> ve.ui.StrongAnnotationTool.static.title =
> OO.ui.deferMsg( 'visualeditor-annotationbutton-strong-tooltip' );
> ve.ui.StrongAnnotationTool.static.annotation = { name: 'textStyle/strong' };
> ve.ui.StrongAnnotationTool.static.commandName = 'strong';
> ve.ui.toolFactory.register( ve.ui.StrongAnnotationTool );
>
> /**
> * UserInterface ins tool.
> *
> * #class
> * #extends ve.ui.AnnotationTool
> * #constructor
> * #param {OO.ui.ToolGroup} toolGroup
> * #param {Object} [config] Configuration options
> */
> ve.ui.InsAnnotationTool = function VeUiInsAnnotationTool( toolGroup, config ) {
> ve.ui.AnnotationTool.call( this, toolGroup, config );
> };
> OO.inheritClass( ve.ui.InsAnnotationTool, ve.ui.AnnotationTool );
> ve.ui.InsAnnotationTool.static.name = 'ins';
> ve.ui.InsAnnotationTool.static.group = 'textStyle';
> ve.ui.InsAnnotationTool.static.icon = 'new';
> ve.ui.InsAnnotationTool.static.title =
> OO.ui.deferMsg( 'visualeditor-annotationbutton-ins-tooltip' );
> ve.ui.InsAnnotationTool.static.annotation = { name: 'textStyle/ins' };
> ve.ui.InsAnnotationTool.static.commandName = 'ins';
> ve.ui.toolFactory.register( ve.ui.InsAnnotationTool );
>
> /**
> * UserInterface highlight tool.
> *
> * #class
> * #extends ve.ui.AnnotationTool
> * #constructor
> * #param {OO.ui.ToolGroup} toolGroup
> * #param {Object} [config] Configuration options
> */
> ve.ui.HighlightAnnotationTool = function VeUiHighlightAnnotationTool( toolGroup, config ) {
> ve.ui.AnnotationTool.call( this, toolGroup, config );
> };
> OO.inheritClass( ve.ui.HighlightAnnotationTool, ve.ui.AnnotationTool );
> ve.ui.HighlightAnnotationTool.static.name = 'highlight';
> ve.ui.HighlightAnnotationTool.static.group = 'textStyle';
> ve.ui.HighlightAnnotationTool.static.icon = 'highlight';
> ve.ui.HighlightAnnotationTool.static.title =
> OO.ui.deferMsg( 'visualeditor-annotationbutton-highlight-tooltip' );
> ve.ui.HighlightAnnotationTool.static.annotation = { name: 'textStyle/highlight' };
> ve.ui.HighlightAnnotationTool.static.commandName = 'highlight';
> ve.ui.toolFactory.register( ve.ui.HighlightAnnotationTool );
>
> /**
diff -r VisualEditor/lib/ve/src/ui/ve.ui.CommandRegistry.js /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/ve.ui.CommandRegistry.js
164a165,182
> 'strong', 'annotation', 'toggle',
> { args: ['textStyle/strong'], supportedSelections: ['linear', 'table'] }
> )
> );
> ve.ui.commandRegistry.register(
> new ve.ui.Command(
> 'ins', 'annotation', 'toggle',
> { args: ['textStyle/ins'], supportedSelections: ['linear', 'table'] }
> )
> );
> ve.ui.commandRegistry.register(
> new ve.ui.Command(
> 'highlight', 'annotation', 'toggle',
> { args: ['textStyle/highlight'], supportedSelections: ['linear', 'table'] }
> )
> );
> ve.ui.commandRegistry.register(
> new ve.ui.Command(
diff -r VisualEditor/lib/ve/src/ui/ve.ui.TriggerRegistry.js /var/www/html/wiki/extensions/VisualEditor/lib/ve/src/ui/ve.ui.TriggerRegistry.js
109,110c109,110
< new ve.ui.Trigger( 'cmd+\\' ),
< new ve.ui.Trigger( 'cmd+m' )
---
> new ve.ui.Trigger( 'cmd+\\' )//,
> //new ve.ui.Trigger( 'cmd+m' )
113,114c113,114
< new ve.ui.Trigger( 'ctrl+\\' ),
< new ve.ui.Trigger( 'ctrl+m' )
---
> new ve.ui.Trigger( 'ctrl+\\' )//,
> //new ve.ui.Trigger( 'ctrl+m' )
125c125,135
< 'strikethrough', { mac: new ve.ui.Trigger( 'cmd+shift+5' ), pc: new ve.ui.Trigger( 'ctrl+shift+5' ) }
---
> 'strong', { mac: new ve.ui.Trigger( 'cmd+h' ), pc: new ve.ui.Trigger( 'ctrl+h' ) }
> );
> ve.ui.triggerRegistry.register(
> 'ins', { mac: new ve.ui.Trigger( 'cmd+e' ), pc: new ve.ui.Trigger( 'ctrl+e' ) }
> );
> ve.ui.triggerRegistry.register(
> 'highlight', { mac: new ve.ui.Trigger( 'cmd+m' ), pc: new ve.ui.Trigger( 'ctrl+m' ) }
> );
> ve.ui.triggerRegistry.register(
> 'strikethrough', { mac: new ve.ui.Trigger( 'cmd+d' ), pc: new ve.ui.Trigger( 'ctrl+d' ) }
diff -r VisualEditor/modules/ve-mw/init/ve.init.mw.Target.js /var/www/html/wiki/extensions/VisualEditor/modules/ve-mw/init/ve.init.mw.Target.js
190,193c190,193
< include: [ { group: 'textStyle' }, 'language', 'clear' ],
< forceExpand: [ 'bold', 'italic', 'clear' ],
< promote: [ 'bold', 'italic' ],
< demote: [ 'strikethrough', 'code', 'underline', 'language', 'clear' ]
---
> include: [ { group: 'textStyle' }, 'strong', 'ins', 'highlight', 'clear' ],
> forceExpand: [ 'strong', 'ins', 'highlight', 'strikethrough', 'clear' ],
> promote: [ 'strong', 'ins', 'highlight', 'strikethrough', 'bold', 'italic', 'underline' ],
> demote: [ 'superscript', 'subscript', 'code', 'clear' ]
diff -r VisualEditor/VisualEditor.php /var/www/html/wiki/extensions/VisualEditor/VisualEditor.php
462a463,464
> 'lib/ve/src/dm/annotations/ve.dm.StrongAnnotation.js',
> 'lib/ve/src/dm/annotations/ve.dm.InsAnnotation.js',
534a537,538
> 'lib/ve/src/ce/annotations/ve.ce.StrongAnnotation.js',
> 'lib/ve/src/ce/annotations/ve.ce.InsAnnotation.js',
706a711,713
> 'visualeditor-annotationbutton-strong-tooltip',
> 'visualeditor-annotationbutton-ins-tooltip',
> 'visualeditor-annotationbutton-highlight-tooltip',
If anyone is interested, I can also post the new classes that I created; finally, I'd be interested to hear any suggestions how to implement something like this in a more maintainable fashion.

Related

CSS class dynamicaly added based on JSON value in React

I have a JSON value based on percentage. Is there a way add different CSS classes based on it?
E.g. If is 10 to 20% then red. If is 20-30 then orange, till 100%.
Assuming you have a json with an array of item inside, you could do smething like this :
const json = [
{
percent: 10
},
{
percent: 35
},
{
percent: 78
}
];
let myClass = "class1";
const setClass = () => {
json.forEach(item => {
if(item.percent >=10 && item.percent <= 35) myClass = 'class2';
if(item.percent > 35 && item.percent <= 78) myClass = 'class3';
})
}
And then set your class as follow :
<input type="text" value="defaultValue" className={myClass} />
fetchFunction = ()=> {
let jsonValueList=parseInt(jsonData);
this.setState({jsonValue: jsonValueList}); }
_.map(this.state.jsonValue, (item, index) => {
<span key={index} className={ (item <= 100 && item >= 90) ? "colorBlack" : (item < 90 && item >= 80) ? "colorGreen" : "..."}></span>})
Could you maybe provide some more information about the JSON or cases (ranges of percentage) in which you need to apply a class?
I see it like that: you have a few classes that are already defined with various names, and for example you have a JSON array that's mapped in JSX — in each entry of the array you have some percentage value according to which you can apply a class. For example:
const json = {
objects: [
{
id: 1,
label: 'Foo',
percentage: 37,
},
{
id: 2,
label: 'Bar',
percentage: 68
},
{
id: 3,
label: 'Biz',
percentage: 12,
}
]
}
const getClassname = (percentage) => {
if (percentage > 0 && percentage <= 33) {
return 'red'
} else if (percentage > 33 && percentage <= 66) {
return 'yellow'
} else if (percentage > 66 && percentage <= 100) {
return 'green'
}
}
const Component = ({label, percentage}) => {
return (
<div className={getClassname(percentage)}>{label}</div>
)
}
const List = ({list}) => {
return {
list.map(entry => (
<Component
key={entry.id}
label={entry.label}
percentage={entry.percentage}
/>
)
}
}
const App = () => {
return (<List list={json.objects}/>)
}
}

Download file feature for all browser in angular 6. Works fine with chrome and all but not in IE. What am I missing?

I have to create a download link in angular 6 that should work in all browser. It works fine with chrome, fire fox but not in IT. I created the blob and changes mumeType too. I might be missing somehting.
Code is below for IE download.
HTML code
<a *ngIf="true" class="link-dwnld" download="{{log.createdBy+'_'+(log.createDate | regexpReplace: '^(.*)Z$': '$1'| date: 'MMddyy_HHmm')+'.csv'}}"
[href]="downloadLinkI" (click)="getInputFileIE(log.overpaymentRecoveryId)">
{{log.createdBy+'_'+(log.createDate | regexpReplace: '^(.*)Z$': '$1'| date: 'MMddyy_HHmm')+'.csv'}}
</a>
ts file code
> getInputFileIE(id: number): boolean {
> console.log('from IE download. its IE');
> this.recoveryLog.forEach( dataStr => {
> if (dataStr.overpaymentRecoveryId === id) {
> this.strI = dataStr.inputFile ;
> this.strO = dataStr.outputFile;
> }
> });
> const strMimeType = 'application/octet-stream;charset=utf-8';
> // IE10+
> if (navigator.msSaveBlob && this.strI) {
> return navigator.msSaveBlob(new Blob(['\ufeff', this.strI], {
> type: strMimeType
> }), this.fileNameI);
> } else if (navigator.msSaveBlob && this.strO) {
> return navigator.msSaveBlob(new Blob(['\ufeff', this.strO], {
> type: strMimeType
> }), this.fileNameO);
> }
> return true; }

PhpStorm v2017.3.4 & Codeception v2.4.0 incompatibility

I'm struggling to make codeception work with PhpStorm.
I have setup CLI Interpreter:
I have also setup Codeception:
And here is the configuration of Codeception:
And when I run the tests from PhpStorm I receive the following:
On the left:
and the text on the right:
Testing started at 00:06 ...
/Applications/MAMP/bin/php/php7.2.1/bin/php /private/var/folders/3g/xng_tnzj6797frqqk20r69mw0000gn/T/ide-codeception.php run --report -o "reporters: report: PhpStorm_Codeception_ReportPrinter" --no-ansi --no-interaction unit
Process finished with exit code 255
but when I run concept run inside my project folder on the terminal, it works fine and I receive this:
What am I doing wrong? I really read the manual on PhpStorm here and videos on youtube, but I can't make it work :/
Thanks!
UPDATE:
On further investigation, I found out the following:
$ /usr/bin/env php --version
PHP 7.2.1 (cli) (built: Jan 15 2018 12:20:50) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2017 Zend Technologies
$ php --version
PHP 7.2.1 (cli) (built: Jan 15 2018 12:20:50) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.2.1, Copyright (c) 1999-2017, by Zend Technologies
with Xdebug v2.6.0beta1, Copyright (c) 2002-2017, by Derick Rethans
$ /usr/bin/env php -c "/Library/Application Support/appsolute/MAMP PRO/conf/php7.2.1.ini" --version
PHP 7.2.1 (cli) (built: Jan 15 2018 12:20:50) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.2.1, Copyright (c) 1999-2017, by Zend Technologies
with Xdebug v2.6.0beta1, Copyright (c) 2002-2017, by Derick Rethans
$ cat .zprofile | grep "alias php="
alias php='/Applications/MAMP/bin/php/php7.2.1/bin/php -c "/Library/Application Support/appsolute/MAMP PRO/conf/php7.2.1.ini"'
UPDATE 2:
While this solved the issue I had with codeception when I was running with code coverage flag --coverage on terminal:
$ which php
php: aliased to /Applications/MAMP/bin/php/php7.2.1/bin/php -c '/Library/Application Support/appsolute/MAMP PRO/conf/php7.2.1.ini'
$ /usr/bin/env php -i | grep "Loaded Configuration File"
Loaded Configuration File => /Applications/MAMP/bin/php/php7.2.1/conf/php.ini
$ mv /Applications/MAMP/bin/php/php7.2.1/conf/php.ini /Applications/MAMP/bin/php/php7.2.1/conf/php.bk.ini
$ ln -s "/Library/Application Support/appsolute/MAMP PRO/conf/php7.2.1.ini" /Applications/MAMP/bin/php/php7.2.1/conf/php.ini
$ /usr/bin/env php --version
PHP 7.2.1 (cli) (built: Jan 15 2018 12:20:50) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.2.1, Copyright (c) 1999-2017, by Zend Technologies
with Xdebug v2.6.0beta1, Copyright (c) 2002-2017, by Derick Rethans
Now I receive the following message on PhpStorm:
Testing started at 15:55 ...
/Applications/MAMP/bin/php/php7.2.1/bin/php /private/var/folders/3g/xng_tnzj6797frqqk20r69mw0000gn/T/ide-codeception.php run --report -o "reporters: report: PhpStorm_Codeception_ReportPrinter" --no-ansi --no-interaction
Fatal error: Class 'PHPUnit_TextUI_ResultPrinter' not found in /private/var/folders/3g/xng_tnzj6797frqqk20r69mw0000gn/T/ide-codeception.php on line 22
Call Stack:
0.0222 528280 1. {main}() /private/var/folders/3g/xng_tnzj6797frqqk20r69mw0000gn/T/ide-codeception.php:0
Process finished with exit code 255
and this is the part of "require-dev" inside my composer.json file
"require-dev": {
"codeception/codeception": "^2.4"
},
based on Fatal error: Class 'PHPUnit_TextUI_ResultPrinter' not found and by seeing the classname PHPUnit_TextUI_ResultPrinter instead of PHPUnit\TextUI\ResultPrinter at that point, I suspect that the phpunit version that codeception 2.4 uses is newer than the one required by PhpStorm? Maybe phpunit v6 is required?
Thanks!
UPDATE 3:
I can now confirm that PhpStorm v2017.3.4 is not compatible with Codeception 2.4 because the later, after v2.4.0 moved to PHPUnit v7.x which PhpStorm v2017.3.4 seems to not be compatible yet. After running:
$ composer remove codeception/codeception
$ composer require codeception/codeception:2.3.9 --dev
I got the following on PhpStorm:
So after more research, I fixed my issue.
simple steps:
$ cd /Applications/PhpStorm.app/Contents/plugins/codeception/lib
$ ls
codeception.jar resources_en.jar
$ mv codeception.jar codeception.zip
$ unzip codeception.zip -d codeception
$ rm codeception.zip
$ cd codeception
$ vim scripts/codeception.php
$ zip -r ../codeception.zip *
$ cd ..
$ mv codeception.zip codeception.jar
$ rm -r codeception/
instead of vim scripts/codeception.php you can use any other text editor you like and replace the contents with the following:
<?php
if (!isset($_SERVER['IDE_CODECEPTION_EXE'])) {
fwrite(STDERR, "The value of Codeception executable is not specified" . PHP_EOL);
exit(1);
}
$exe = realpath($_SERVER['IDE_CODECEPTION_EXE']);
if (!file_exists($exe)) {
$originalPath = $_SERVER['IDE_CODECEPTION_EXE'];
fwrite(STDERR, "The value of Codeception executable is specified, but file doesn't exist '$originalPath'" . PHP_EOL);
exit(1);
}
if (Phar::isValidPharFilename(basename($exe), true)) {
require_once 'phar://' . $exe . '/autoload.php';
}
else {
require_once dirname($exe) .'/autoload.php';
}
class PhpStorm_Codeception_ReportPrinter extends \PHPUnit\TextUI\ResultPrinter
{
protected $testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED;
protected $failures = [];
/**
* #var bool
*/
private $isSummaryTestCountPrinted = false;
/**
* #var string
*/
private $startedTestName;
/**
* #var string
*/
private $flowId;
/**
* #param string $progress
*/
protected function writeProgress($progress): void
{
}
/**
* #param \PHPUnit\Framework\TestResult $result
*/
public function printResult(\PHPUnit\Framework\TestResult $result) : void
{
$this->printHeader();
$this->printFooter($result);
}
/**
* An error occurred.
*
* #param \PHPUnit\Framework\Test $test
* #param \Throwable $e
* #param float $time
*/
public function addError(\PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
{
$this->addFail(\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, $test, $e);
}
/**
* A warning occurred.
*
* #param \PHPUnit\Framework\Test $test
* #param \PHPUnit\Framework\Warning $e
* #param float $time
*
* #since Method available since Release 5.1.0
*/
public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time): void
{
$this->addFail(\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, $test, $e);
}
/**
* A failure occurred.
*
* #param \PHPUnit\Framework\Test $test
* #param \PHPUnit\Framework\AssertionFailedError $e
* #param float $time
*/
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time): void
{
$parameters = [];
if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) {
$comparisonFailure = $e->getComparisonFailure();
if ($comparisonFailure instanceof \SebastianBergmann\Comparator\ComparisonFailure) {
$expectedString = $comparisonFailure->getExpectedAsString();
if (is_null($expectedString) || empty($expectedString)) {
$expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
}
$actualString = $comparisonFailure->getActualAsString();
if (is_null($actualString) || empty($actualString)) {
$actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
}
if (!is_null($actualString) && !is_null($expectedString)) {
$parameters['type'] = 'comparisonFailure';
$parameters['actual'] = $actualString;
$parameters['expected'] = $expectedString;
}
}
}
$this->addFail(\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, $test, $e, $parameters);
}
/**
* Incomplete test.
*
* #param \PHPUnit\Framework\Test $test
* #param \Throwable $e
* #param float $time
*/
public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
{
$this->addIgnoredTest($test, $e);
}
/**
* Risky test.
*
* #param \PHPUnit\Framework\Test $test
* #param \Throwable $e
* #param float $time
*/
public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
{
$this->addError($test, $e, $time);
}
/**
* Skipped test.
*
* #param \PHPUnit\Framework\Test $test
* #param \Throwable $e
* #param float $time
*/
public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
{
$testName = self::getTestAsString($test);
if ($this->startedTestName != $testName) {
$this->startTest($test);
$this->printEvent(
'testIgnored',
[
'name' => $testName,
'message' => self::getMessage($e),
'details' => self::getDetails($e),
]
);
$this->endTest($test, $time);
} else {
$this->addIgnoredTest($test, $e);
}
}
public function addIgnoredTest(\PHPUnit\Framework\Test $test, Exception $e) {
$this->addFail(\PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, $test, $e);
}
private function addFail($status, \PHPUnit\Framework\Test $test, $e, $parameters = []) {
$key = self::getTestSignature($test);
$this->testStatus = $status;
$parameters['message'] = self::getMessage($e);
$parameters['details'] = self::getDetails($e);
$this->failures[$key][] = $parameters;
}
/**
* A testsuite started.
*
* #param \PHPUnit\Framework\TestSuite $suite
*/
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite): void
{
if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
$this->flowId = getmypid();
} else {
$this->flowId = false;
}
if (!$this->isSummaryTestCountPrinted) {
$this->isSummaryTestCountPrinted = true;
$this->printEvent(
'testCount',
['count' => count($suite)]
);
}
$suiteName = $suite->getName();
if (empty($suiteName)) {
return;
}
//TODO: configure 'locationHint' to navigate to 'unit', 'acceptance', 'functional' test suite
//TODO: configure 'locationHint' to navigate to DataProvider tests for Codeception earlier 2.2.6
$parameters = ['name' => $suiteName];
$this->printEvent('testSuiteStarted', $parameters);
}
/**
* A testsuite ended.
*
* #param \PHPUnit\Framework\TestSuite $suite
*/
public function endTestSuite(\PHPUnit\Framework\TestSuite $suite): void
{
$suiteName = $suite->getName();
if (empty($suiteName)) {
return;
}
$parameters = ['name' => $suiteName];
$this->printEvent('testSuiteFinished', $parameters);
}
public static function getTestSignature(\PHPUnit\Framework\SelfDescribing $testCase)
{
if ($testCase instanceof Codeception\Test\Interfaces\Descriptive) {
return $testCase->getSignature();
}
if ($testCase instanceof \PHPUnit\Framework\TestCase) {
return get_class($testCase) . ':' . $testCase->getName(false);
}
return $testCase->toString();
}
public static function getTestAsString(\PHPUnit\Framework\SelfDescribing $testCase)
{
if ($testCase instanceof Codeception\Test\Interfaces\Descriptive) {
return $testCase->toString();
}
if ($testCase instanceof \PHPUnit\Framework\TestCase) {
$text = $testCase->getName();
$text = preg_replace('/([A-Z]+)([A-Z][a-z])/', '\\1 \\2', $text);
$text = preg_replace('/([a-z\d])([A-Z])/', '\\1 \\2', $text);
$text = preg_replace('/^test /', '', $text);
$text = ucfirst(strtolower($text));
$text = str_replace(['::', 'with data set'], [':', '|'], $text);
return Codeception\Util\ReflectionHelper::getClassShortName($testCase) . ': ' . $text;
}
return $testCase->toString();
}
public static function getTestFileName(\PHPUnit\Framework\SelfDescribing $testCase)
{
if ($testCase instanceof Codeception\Test\Interfaces\Descriptive) {
return $testCase->getFileName();
}
return (new \ReflectionClass($testCase))->getFileName();
}
public static function getTestFullName(\PHPUnit\Framework\SelfDescribing $testCase)
{
if ($testCase instanceof Codeception\Test\Interfaces\Plain) {
return self::getTestFileName($testCase);
}
if ($testCase instanceof Codeception\Test\Interfaces\Descriptive) {
$signature = $testCase->getSignature(); // cut everything before ":" from signature
return self::getTestFileName($testCase) . '::' . preg_replace('~^(.*?):~', '', $signature);
}
if ($testCase instanceof \PHPUnit\Framework\TestCase) {
return self::getTestFileName($testCase) . '::' . $testCase->getName(false);
}
return self::getTestFileName($testCase) . '::' . $testCase->toString();
}
/**
* A test started.
*
* #param \PHPUnit\Framework\Test $test
*/
public function startTest(\PHPUnit\Framework\Test $test): void
{
$testName = self::getTestAsString($test);
$this->startedTestName = $testName;
$location = "php_qn://" . self::getTestFullName($test);
$gherkin = self::getGherkinTestLocation($test);
if ($gherkin != null) {
$location = $gherkin;
}
$params = ['name' => $testName, 'locationHint' => $location];
if ($test instanceof \Codeception\Test\Interfaces\ScenarioDriven) {
$this->printEvent('testSuiteStarted', $params);
}
else {
$this->printEvent('testStarted', $params);
}
}
/**
* A test ended.
*
* #param \PHPUnit\Framework\Test $test
* #param float $time
*/
public function endTest(\PHPUnit\Framework\Test $test, float $time): void
{
$result = null;
switch ($this->testStatus) {
case \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR:
case \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE:
$result = 'testFailed';
break;
case \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED:
$result = 'testIgnored';
break;
}
$name = self::getTestAsString($test);
if ($this->startedTestName != $name) {
$name = $this->startedTestName;
}
$gherkin = self::getGherkinTestLocation($test);
$duration = (int)(round($time, 2) * 1000);
if ($test instanceof \Codeception\Test\Interfaces\ScenarioDriven) {
$steps = $test->getScenario()->getSteps();
$len = sizeof($steps);
$printed = 0;
for ($i = 0; $i < $len; $i++) {
$step = $steps[$i];
if ($step->getAction() == null && $step->getMetaStep()) {
$step = $step->getMetaStep();
}
if ($step instanceof \Codeception\Step\Comment) {
// TODO: render comments in grey color?
// comments are not shown because at the moment it's hard to distinguish them from real tests.
// e.g. comment steps show descriptions from *.feature tests.
continue;
}
$printed++;
$testName = sprintf('%s %s %s',
ucfirst($step->getPrefix()),
$step->getHumanizedActionWithoutArguments(),
$step->getHumanizedArguments()
);
$location = $gherkin != null ? $gherkin : $step->getLine();
$this->printEvent('testStarted',
[
'name' => $testName,
'locationHint' => "file://$location"
]);
$params = ['name' => $testName];
if ($i == $len - 1) {
parent::endTest($test, $time);
$this->printError($test, $result, $testName);
$params['duration'] = $duration;
}
$this->printEvent('testFinished', $params);
}
if ($printed == 0 && $result != null) {
$this->printEvent('testStarted', ['name' => $name]);
parent::endTest($test, $time);
$this->printError($test, $result, $name);
$this->printEvent('testFinished', [
'name' => $name,
'duration' => $duration
]);
}
$this->printEvent('testSuiteFinished', ['name' => $name]);
}
else {
parent::endTest($test, $time);
$this->printError($test, $result, self::getTestAsString($test));
$this->printEvent(
'testFinished',
[
'name' => self::getTestAsString($test),
'duration' => $duration
]
);
}
}
private function printError(\PHPUnit\Framework\Test $test, $result, $name) {
if ($result != null) {
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED;
$key = self::getTestSignature($test);
if (isset($this->failures[$key])) {
$failures = $this->failures[$key];
//TODO: check if it's possible to have sizeof($params) > 1
assert(sizeof($failures) == 1);
$params = $failures[0];
$params['name'] = $name;
$this->printEvent($result, $params);
unset($this->failures[$key]);
}
}
}
/**
* #param string $eventName
* #param array $params
*/
private function printEvent($eventName, $params = [])
{
$this->write("\n##teamcity[$eventName");
if ($this->flowId) {
$params['flowId'] = $this->flowId;
}
foreach ($params as $key => $value) {
$escapedValue = self::escapeValue($value);
$this->write(" $key='$escapedValue'");
}
$this->write("]\n");
}
private static function getGherkinTestLocation(\PHPUnit\Framework\Test $test) {
if ($test instanceof \Codeception\Test\Gherkin) {
$feature = $test->getFeatureNode();
$scenario = $test->getScenarioNode();
if ($feature != null && $scenario != null) {
return "file://" . $test->getFeatureNode()->getFile() . ":" . $test->getScenarioNode()->getLine();
}
}
return null;
}
/**
* #param Exception $e
*
* #return string
*/
private static function getMessage(Exception $e)
{
$message = '';
if (!$e instanceof \PHPUnit\Framework\Exception) {
if (strlen(get_class($e)) != 0) {
$message = $message . get_class($e);
}
if (strlen($message) != 0 && strlen($e->getMessage()) != 0) {
$message = $message . ' : ';
}
}
return $message . $e->getMessage();
}
/**
* #param Exception $e
*
* #return string
*/
private static function getDetails(Exception $e)
{
$stackTrace = \PHPUnit\Util\Filter::getFilteredStacktrace($e);
$previous = $e->getPrevious();
while ($previous) {
$stackTrace .= "\nCaused by\n" .
\PHPUnit\Framework\TestFailure::exceptionToString($previous) . "\n" .
\PHPUnit\Util\Filter::getFilteredStacktrace($previous);
$previous = $previous->getPrevious();
}
return ' ' . str_replace("\n", "\n ", $stackTrace);
}
/**
* #param mixed $value
*
* #return string
*/
private static function getPrimitiveValueAsString($value)
{
if (is_null($value)) {
return 'null';
} elseif (is_bool($value)) {
return $value == true ? 'true' : 'false';
} elseif (is_scalar($value)) {
return print_r($value, true);
}
return;
}
/**
* #param $text
*
* #return string
*/
private static function escapeValue($text)
{
$text = str_replace('|', '||', $text);
$text = str_replace("'", "|'", $text);
$text = str_replace("\n", '|n', $text);
$text = str_replace("\r", '|r', $text);
$text = str_replace(']', '|]', $text);
$text = str_replace('[', '|[', $text);
return $text;
}
/**
* #param string $className
*
* #return string
*/
private static function getFileName($className)
{
$reflectionClass = new ReflectionClass($className);
$fileName = $reflectionClass->getFileName();
return $fileName;
}
}
$app = new \Codeception\Application('Codeception', \Codeception\Codecept::VERSION);
if (version_compare(\Codeception\Codecept::VERSION, "2.2.6") >= 0) {
$app->add(new \Codeception\Command\Run('run'));
$app->run();
}
else {
class PhpStorm_Codeception_Command_Run extends \Codeception\Command\Run {
public function execute(\Symfony\Component\Console\Input\InputInterface $input,
\Symfony\Component\Console\Output\OutputInterface $output)
{
$this->ensureCurlIsAvailable();
$this->options = $input->getOptions();
$this->output = $output;
$config = \Codeception\Configuration::config($this->options['config']);
if (!$this->options['colors']) {
$this->options['colors'] = $config['settings']['colors'];
}
if (!$this->options['silent']) {
$this->output->writeln(
\Codeception\Codecept::versionString() . "\nPowered by " . \PHPUnit\Runner\Version::getVersionString()
);
}
if ($this->options['debug']) {
$this->output->setVerbosity(\Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE);
}
$userOptions = array_intersect_key($this->options, array_flip($this->passedOptionKeys($input)));
$userOptions = array_merge(
$userOptions,
$this->booleanOptions($input, ['xml', 'html', 'json', 'tap', 'coverage', 'coverage-xml', 'coverage-html'])
);
$userOptions['verbosity'] = $this->output->getVerbosity();
$userOptions['interactive'] = !$input->hasParameterOption(['--no-interaction', '-n']);
$userOptions['ansi'] = (!$input->hasParameterOption('--no-ansi') xor $input->hasParameterOption('ansi'));
if ($this->options['no-colors'] || !$userOptions['ansi']) {
$userOptions['colors'] = false;
}
if ($this->options['group']) {
$userOptions['groups'] = $this->options['group'];
}
if ($this->options['skip-group']) {
$userOptions['excludeGroups'] = $this->options['skip-group'];
}
if ($this->options['report']) {
$userOptions['silent'] = true;
}
if ($this->options['coverage-xml'] or $this->options['coverage-html'] or $this->options['coverage-text']) {
$this->options['coverage'] = true;
}
if (!$userOptions['ansi'] && $input->getOption('colors')) {
$userOptions['colors'] = true; // turn on colors even in non-ansi mode if strictly passed
}
$suite = $input->getArgument('suite');
$test = $input->getArgument('test');
if (! \Codeception\Configuration::isEmpty() && ! $test && strpos($suite, $config['paths']['tests']) === 0) {
list(, $suite, $test) = $this->matchTestFromFilename($suite, $config['paths']['tests']);
}
if ($this->options['group']) {
$this->output->writeln(sprintf("[Groups] <info>%s</info> ", implode(', ', $this->options['group'])));
}
if ($input->getArgument('test')) {
$this->options['steps'] = true;
}
if ($test) {
$filter = $this->matchFilteredTestName($test);
$userOptions['filter'] = $filter;
}
$this->codecept = new PhpStorm_Codeception_Codecept($userOptions);
if ($suite and $test) {
$this->codecept->run($suite, $test);
}
if (!$test) {
$suites = $suite ? explode(',', $suite) : \Codeception\Configuration::suites();
$this->executed = $this->runSuites($suites, $this->options['skip']);
if (!empty($config['include']) and !$suite) {
$current_dir = \Codeception\Configuration::projectDir();
$suites += $config['include'];
$this->runIncludedSuites($config['include'], $current_dir);
}
if ($this->executed === 0) {
throw new \RuntimeException(
sprintf("Suite '%s' could not be found", implode(', ', $suites))
);
}
}
$this->codecept->printResult();
if (!$input->getOption('no-exit')) {
if (!$this->codecept->getResult()->wasSuccessful()) {
exit(1);
}
}
}
private function matchFilteredTestName(&$path)
{
if (version_compare(\Codeception\Codecept::VERSION, "2.2.5") >= 0) {
$test_parts = explode(':', $path, 2);
if (count($test_parts) > 1) {
list($path, $filter) = $test_parts;
// use carat to signify start of string like in normal regex
// phpunit --filter matches against the fully qualified method name, so tests actually begin with :
$carat_pos = strpos($filter, '^');
if ($carat_pos !== false) {
$filter = substr_replace($filter, ':', $carat_pos, 1);
}
return $filter;
}
return null;
}
else {
$test_parts = explode(':', $path);
if (count($test_parts) > 1) {
list($path, $filter) = $test_parts;
return $filter;
}
return null;
}
}
private function ensureCurlIsAvailable()
{
if (!extension_loaded('curl')) {
throw new \Exception(
"Codeception requires CURL extension installed to make tests run\n"
. "If you are not sure, how to install CURL, please refer to StackOverflow\n\n"
. "Notice: PHP for Apache/Nginx and CLI can have different php.ini files.\n"
. "Please make sure that your PHP you run from console has CURL enabled."
);
}
}
}
class PhpStorm_Codeception_Codecept extends \Codeception\Codecept {
public function __construct($options = [])
{
parent::__construct($options);
$printer = new PhpStorm_Codeception_ReportPrinter();
$this->runner = new \Codeception\PHPUnit\Runner();
$this->runner->setPrinter($printer);
}
}
$app->add(new PhpStorm_Codeception_Command_Run('run'));
$app->run();
}
and now it works fine with PHPUnit v7.x and Codeception 2.4.x
UPDATE:
I have uploaded here a jar file that fixes the issue, until PhpStorm fixes it officially, for anyone who don't want to go through the above steps.
UPDATE 2:
Just want to point out that this fix, does not provide backwards compatibility for versions of codeception less than 2.4.0, I'm sure PhpStorm team will provide a more elegant solution that would allow having projects that use codeception 2.4.x and other projects that use earlier verions.
I had same issue as mentioned here. Updating PHPStorm to version 2017.3.6 fixed this (also updated all dependecies for PHPStorm e.g. Symfony plugins PHPUnit plugins etc.)
The error Fatal error: Class 'PHPUnit_TextUI_ResultPrinter' can also happen after upgrading PHPUnit to a newer version.
I had to manually update PHPStorm detected version to fix this issue:
Update PHPSTORM to new version resolved issue

Loop through JavaScript Object but keep key with value

var data = {
2016-09-24: {
0: {amount: 200, id: 2},
1: {...}
},
2016-09-25: {
0: {amount: 500, id: 8},
1: {...}
}
}
I want to represent the above data in a view like:
"**" would be a div with a card class:
*****************************************
* <h2>2016-09-24</h2> *
* *
* <li>amount: 200</li> *
* <li>amount: 40</li> *
* *
*****************************************
*****************************************
* <h2>2016-09-25</h2> *
* *
* <li>amount: 500</li> *
* <li>amount: 90</li> *
* *
*****************************************
I have yet to reach the layout but stuck at the loop. Im using React es6:
dailySales(){
Object.keys(data).forEach(function(key) {
var dates = key;
var val = data[key];
let sales = val.map(function(s, i) {
//console.log(s.amount);
});
});
}
The above commented out console.log would return all amount. How to segment each value with the date (key)? This question is similar to this one.
In the wise words of Leonardo DiCaprio in "Inception", "we need to go deeper"
You're super close and on the right track. You need to loop over the nested objects again and you'll get what you're looking for:
Object.keys( data ).map( function( date, i ) {
// here you have the date
return (
<div key={ i }>
<h1>{ date }</h1>
{ Object.keys( data[ date ] ).map( function( item, j ) {
// and here you have the item by its key
var rowItem = data[date][item];
return (
<p key={ rowItem.id }>Amount: { rowItem.amount }</p>
);
})}
</div>
);
});
https://jsfiddle.net/64s0yvvz/1/

PHP mysql order by is not working as expected

I am trying to display data from mysql data base in HTLM-css (used bootstrap) tabel. But the query result is not displaying as expected in the order of 'ID'. Can somebody help me to solve my issue?
Thanks in advance.
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" id="example">
<div class="alert alert-info">
<strong><center>Delete Multiple Data:Check the Box and click the Delete button to Delete Data </strong></center>
</div>
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th>Value Date</th>
<th>Type</th>
<th>Amount</th>
<th>Donated By</th>
<th>Paid To</th>
</tr>
</thead>
<tbody>
<?php
$query=mysql_query("select * from temple_txn order by id desc")or die(mysql_error());
while($row=mysql_fetch_array($query)){
$id=$row['id'];
?>
<tr>
<td><input name="selector[]" type="checkbox" value="<?php echo $id; ?>"></td>
<td><?php echo $row['id'] ?></td>
<td><?php echo $row['value_date'] ?></td>
<td><?php echo $row['txn_type'] ?></td>
<td><?php echo $row['txn_amount'] ?></td>
<td><?php echo $row['donated_by'] ?></td>
<td><?php echo $row['paid_to'] ?></td>
</tr>
<?php } ?>
</tbody>
</table>
(JS DT_bootstrap)
/* Set the defaults for DataTables initialisation */
$.extend( true, $.fn.dataTable.defaults, {
"sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>",
"sPaginationType": "bootstrap",
"oLanguage": {
"sLengthMenu": "_MENU_ records per page"
}
} );
/* Default class modification */
$.extend( $.fn.dataTableExt.oStdClasses, {
"sWrapper": "dataTables_wrapper form-inline"
} );
/* API method to get paging information */
$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
{
return {
"iStart": oSettings._iDisplayStart,
"iEnd": oSettings.fnDisplayEnd(),
"iLength": oSettings._iDisplayLength,
"iTotal": oSettings.fnRecordsTotal(),
"iFilteredTotal": oSettings.fnRecordsDisplay(),
"iPage": oSettings._iDisplayLength === -1 ?
0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
"iTotalPages": oSettings._iDisplayLength === -1 ?
0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
};
};
/* Bootstrap style pagination control */
$.extend( $.fn.dataTableExt.oPagination, {
"bootstrap": {
"fnInit": function( oSettings, nPaging, fnDraw ) {
var oLang = oSettings.oLanguage.oPaginate;
var fnClickHandler = function ( e ) {
e.preventDefault();
if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
fnDraw( oSettings );
}
};
$(nPaging).addClass('pagination').append(
'<ul>'+
'<li class="prev disabled">← '+oLang.sPrevious+'</li>'+
'<li class="next disabled">'+oLang.sNext+' → </li>'+
'</ul>'
);
var els = $('a', nPaging);
$(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
$(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
},
"fnUpdate": function ( oSettings, fnDraw ) {
var iListLength = 5;
var oPaging = oSettings.oInstance.fnPagingInfo();
var an = oSettings.aanFeatures.p;
var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
if ( oPaging.iTotalPages < iListLength) {
iStart = 1;
iEnd = oPaging.iTotalPages;
}
else if ( oPaging.iPage <= iHalf ) {
iStart = 1;
iEnd = iListLength;
} else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) {
iStart = oPaging.iTotalPages - iListLength + 1;
iEnd = oPaging.iTotalPages;
} else {
iStart = oPaging.iPage - iHalf + 1;
iEnd = iStart + iListLength - 1;
}
for ( i=0, ien=an.length ; i<ien ; i++ ) {
// Remove the middle elements
$('li:gt(0)', an[i]).filter(':not(:last)').remove();
// Add the new list items and their event handlers
for ( j=iStart ; j<=iEnd ; j++ ) {
sClass = (j==oPaging.iPage+1) ? 'class="active"' : '';
$('<li '+sClass+'>'+j+'</li>')
.insertBefore( $('li:last', an[i])[0] )
.bind('click', function (e) {
e.preventDefault();
oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
fnDraw( oSettings );
} );
}
// Add / remove disabled classes from the static elements
if ( oPaging.iPage === 0 ) {
$('li:first', an[i]).addClass('disabled');
} else {
$('li:first', an[i]).removeClass('disabled');
}
if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
$('li:last', an[i]).addClass('disabled');
} else {
$('li:last', an[i]).removeClass('disabled');
}
}
}
}
} );
/*
* TableTools Bootstrap compatibility
* Required TableTools 2.1+
*/
if ( $.fn.DataTable.TableTools ) {
// Set the classes that TableTools uses to something suitable for Bootstrap
$.extend( true, $.fn.DataTable.TableTools.classes, {
"container": "DTTT btn-group",
"buttons": {
"normal": "btn",
"disabled": "disabled"
},
"collection": {
"container": "DTTT_dropdown dropdown-menu",
"buttons": {
"normal": "",
"disabled": "disabled"
}
},
"print": {
"info": "DTTT_print_info modal"
},
"select": {
"row": "active"
}
} );
// Have the collection use a bootstrap compatible dropdown
$.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, {
"collection": {
"container": "ul",
"button": "li",
"liner": "a"
}
} );
}
/* Table initialisation */
$(document).ready(function() {
$('#example').dataTable( {
"sPaginationType": "bootstrap",
"oLanguage": {
}
} );
} );
It seems that you fetch the data from your database in the correct order. Based on our conversation in the question's comments, I think that your problem is caused by the JS library DataTables or the bootstrap plugin after pageload. To ensure this, deactivate the JavaScript and see if the rows are in correct order if they are not touched by the plugin.
If DataTables tries to sort by the values in the first column (you have some HTML in there), this could explain the seemingly random sorting. Try setting an implicit order/sort setting (reference). Your ID is in the second column and the column index is zero-based, so this would be it:
$(document).ready(function() {
$('#example').dataTable( {
"sPaginationType": "bootstrap",
"oLanguage": {},
"aaSorting": [[1,'desc']]
});
});