diff options
author | Jeremy Benoist <j0k3r@users.noreply.github.com> | 2015-12-06 14:31:26 +0100 |
---|---|---|
committer | Jeremy Benoist <j0k3r@users.noreply.github.com> | 2015-12-06 14:31:26 +0100 |
commit | a7f1921f7db312b5def3839393357f443dcbb52c (patch) | |
tree | 0522e03891433e9fdc9eb64d52b2d9651aadf1f7 | |
parent | 2e15e30bf0e634bbbc3a9678904953d015490ed2 (diff) | |
parent | 752b90d1f2e279d3662d5431b09c7587df2937ca (diff) | |
download | wallabag-a7f1921f7db312b5def3839393357f443dcbb52c.tar.gz wallabag-a7f1921f7db312b5def3839393357f443dcbb52c.tar.zst wallabag-a7f1921f7db312b5def3839393357f443dcbb52c.zip |
Merge pull request #1478 from K-Phoen/rule-based-tags
Rule based tags
26 files changed, 1888 insertions, 34 deletions
diff --git a/app/AppKernel.php b/app/AppKernel.php index 2475fe16..1eacb348 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php | |||
@@ -29,6 +29,7 @@ class AppKernel extends Kernel | |||
29 | new FOS\OAuthServerBundle\FOSOAuthServerBundle(), | 29 | new FOS\OAuthServerBundle\FOSOAuthServerBundle(), |
30 | new Wallabag\UserBundle\WallabagUserBundle(), | 30 | new Wallabag\UserBundle\WallabagUserBundle(), |
31 | new Scheb\TwoFactorBundle\SchebTwoFactorBundle(), | 31 | new Scheb\TwoFactorBundle\SchebTwoFactorBundle(), |
32 | new KPhoen\RulerZBundle\KPhoenRulerZBundle(), | ||
32 | ); | 33 | ); |
33 | 34 | ||
34 | if (in_array($this->getEnvironment(), array('dev', 'test'))) { | 35 | if (in_array($this->getEnvironment(), array('dev', 'test'))) { |
diff --git a/app/config/config.yml b/app/config/config.yml index 285fbd7c..82c5e7c9 100644 --- a/app/config/config.yml +++ b/app/config/config.yml | |||
@@ -189,3 +189,7 @@ scheb_two_factor: | |||
189 | sender_email: %twofactor_sender% | 189 | sender_email: %twofactor_sender% |
190 | digits: 6 | 190 | digits: 6 |
191 | template: WallabagUserBundle:Authentication:form.html.twig | 191 | template: WallabagUserBundle:Authentication:form.html.twig |
192 | |||
193 | kphoen_rulerz: | ||
194 | executors: | ||
195 | doctrine: true | ||
diff --git a/composer.json b/composer.json index 5892dc1a..26608cdc 100644 --- a/composer.json +++ b/composer.json | |||
@@ -57,7 +57,8 @@ | |||
57 | "friendsofsymfony/oauth-server-bundle": "^1.4@dev", | 57 | "friendsofsymfony/oauth-server-bundle": "^1.4@dev", |
58 | "scheb/two-factor-bundle": "~1.4", | 58 | "scheb/two-factor-bundle": "~1.4", |
59 | "grandt/phpepub": "~4.0", | 59 | "grandt/phpepub": "~4.0", |
60 | "wallabag/php-mobi": "~1.0.0" | 60 | "wallabag/php-mobi": "~1.0.0", |
61 | "kphoen/rulerz-bundle": "~0.10" | ||
61 | }, | 62 | }, |
62 | "require-dev": { | 63 | "require-dev": { |
63 | "doctrine/doctrine-fixtures-bundle": "~2.2.0", | 64 | "doctrine/doctrine-fixtures-bundle": "~2.2.0", |
diff --git a/composer.lock b/composer.lock index b7b5d142..eea3f8a0 100644 --- a/composer.lock +++ b/composer.lock | |||
@@ -4,7 +4,8 @@ | |||
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", | 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", |
5 | "This file is @generated automatically" | 5 | "This file is @generated automatically" |
6 | ], | 6 | ], |
7 | "hash": "a9ec461e17166dcda1563dd55f6ff861", | 7 | "hash": "fed9468f6c830b0f81899daad7670af7", |
8 | "content-hash": "394f8a6ca5162f2d2756dbbee0ff5aae", | ||
8 | "packages": [ | 9 | "packages": [ |
9 | { | 10 | { |
10 | "name": "doctrine/annotations", | 11 | "name": "doctrine/annotations", |
@@ -1485,6 +1486,610 @@ | |||
1485 | "time": "2014-10-12 19:18:40" | 1486 | "time": "2014-10-12 19:18:40" |
1486 | }, | 1487 | }, |
1487 | { | 1488 | { |
1489 | "name": "hoa/compiler", | ||
1490 | "version": "2.15.10.29", | ||
1491 | "source": { | ||
1492 | "type": "git", | ||
1493 | "url": "https://github.com/hoaproject/Compiler.git", | ||
1494 | "reference": "ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39" | ||
1495 | }, | ||
1496 | "dist": { | ||
1497 | "type": "zip", | ||
1498 | "url": "https://api.github.com/repos/hoaproject/Compiler/zipball/ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39", | ||
1499 | "reference": "ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39", | ||
1500 | "shasum": "" | ||
1501 | }, | ||
1502 | "require": { | ||
1503 | "hoa/core": "~2.0", | ||
1504 | "hoa/file": "~0.0", | ||
1505 | "hoa/iterator": "~1.0", | ||
1506 | "hoa/math": "~0.0", | ||
1507 | "hoa/regex": "~0.0", | ||
1508 | "hoa/visitor": "~1.0" | ||
1509 | }, | ||
1510 | "require-dev": { | ||
1511 | "hoa/json": "~1.0", | ||
1512 | "hoa/test": "~1.0" | ||
1513 | }, | ||
1514 | "type": "library", | ||
1515 | "extra": { | ||
1516 | "branch-alias": { | ||
1517 | "dev-master": "2.x-dev" | ||
1518 | } | ||
1519 | }, | ||
1520 | "autoload": { | ||
1521 | "psr-4": { | ||
1522 | "Hoa\\Compiler\\": "." | ||
1523 | } | ||
1524 | }, | ||
1525 | "notification-url": "https://packagist.org/downloads/", | ||
1526 | "license": [ | ||
1527 | "BSD-3-Clause" | ||
1528 | ], | ||
1529 | "authors": [ | ||
1530 | { | ||
1531 | "name": "Ivan Enderlin", | ||
1532 | "email": "ivan.enderlin@hoa-project.net" | ||
1533 | }, | ||
1534 | { | ||
1535 | "name": "Hoa community", | ||
1536 | "homepage": "http://hoa-project.net/" | ||
1537 | } | ||
1538 | ], | ||
1539 | "description": "The Hoa\\Compiler library.", | ||
1540 | "homepage": "http://hoa-project.net/", | ||
1541 | "keywords": [ | ||
1542 | "algebraic", | ||
1543 | "ast", | ||
1544 | "compiler", | ||
1545 | "context-free", | ||
1546 | "coverage", | ||
1547 | "exhaustive", | ||
1548 | "grammar", | ||
1549 | "isotropic", | ||
1550 | "language", | ||
1551 | "lexer", | ||
1552 | "library", | ||
1553 | "ll1", | ||
1554 | "llk", | ||
1555 | "parser", | ||
1556 | "pp", | ||
1557 | "random", | ||
1558 | "regular", | ||
1559 | "rule", | ||
1560 | "sampler", | ||
1561 | "syntax", | ||
1562 | "token", | ||
1563 | "trace", | ||
1564 | "uniform" | ||
1565 | ], | ||
1566 | "time": "2015-10-29 21:35:12" | ||
1567 | }, | ||
1568 | { | ||
1569 | "name": "hoa/core", | ||
1570 | "version": "2.15.11.09", | ||
1571 | "source": { | ||
1572 | "type": "git", | ||
1573 | "url": "https://github.com/hoaproject/Core.git", | ||
1574 | "reference": "5538b1e90e2c66c90df5cc45e03fb85d047be900" | ||
1575 | }, | ||
1576 | "dist": { | ||
1577 | "type": "zip", | ||
1578 | "url": "https://api.github.com/repos/hoaproject/Core/zipball/5538b1e90e2c66c90df5cc45e03fb85d047be900", | ||
1579 | "reference": "5538b1e90e2c66c90df5cc45e03fb85d047be900", | ||
1580 | "shasum": "" | ||
1581 | }, | ||
1582 | "require": { | ||
1583 | "ext-spl": "*", | ||
1584 | "php": ">=5.4.0" | ||
1585 | }, | ||
1586 | "require-dev": { | ||
1587 | "hoa/test": "~1.0" | ||
1588 | }, | ||
1589 | "suggest": { | ||
1590 | "ext-mbstring": "ext/mbstring must be present (or a third implementation).", | ||
1591 | "hoa/cli": "To use the `hoa` script." | ||
1592 | }, | ||
1593 | "type": "library", | ||
1594 | "extra": { | ||
1595 | "branch-alias": { | ||
1596 | "dev-master": "2.x-dev" | ||
1597 | } | ||
1598 | }, | ||
1599 | "autoload": { | ||
1600 | "psr-4": { | ||
1601 | "Hoa\\Core\\": "." | ||
1602 | }, | ||
1603 | "files": [ | ||
1604 | "Core.php" | ||
1605 | ] | ||
1606 | }, | ||
1607 | "notification-url": "https://packagist.org/downloads/", | ||
1608 | "license": [ | ||
1609 | "BSD-3-Clause" | ||
1610 | ], | ||
1611 | "authors": [ | ||
1612 | { | ||
1613 | "name": "Ivan Enderlin", | ||
1614 | "email": "ivan.enderlin@hoa-project.net" | ||
1615 | }, | ||
1616 | { | ||
1617 | "name": "Hoa community", | ||
1618 | "homepage": "http://hoa-project.net/" | ||
1619 | } | ||
1620 | ], | ||
1621 | "description": "The Hoa\\Core library.", | ||
1622 | "homepage": "http://hoa-project.net/", | ||
1623 | "keywords": [ | ||
1624 | "consistency", | ||
1625 | "core", | ||
1626 | "data", | ||
1627 | "event", | ||
1628 | "library", | ||
1629 | "listener", | ||
1630 | "parameter", | ||
1631 | "protocol" | ||
1632 | ], | ||
1633 | "time": "2015-11-09 06:51:06" | ||
1634 | }, | ||
1635 | { | ||
1636 | "name": "hoa/file", | ||
1637 | "version": "0.15.11.09", | ||
1638 | "source": { | ||
1639 | "type": "git", | ||
1640 | "url": "https://github.com/hoaproject/File.git", | ||
1641 | "reference": "f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee" | ||
1642 | }, | ||
1643 | "dist": { | ||
1644 | "type": "zip", | ||
1645 | "url": "https://api.github.com/repos/hoaproject/File/zipball/f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee", | ||
1646 | "reference": "f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee", | ||
1647 | "shasum": "" | ||
1648 | }, | ||
1649 | "require": { | ||
1650 | "hoa/core": "~2.0", | ||
1651 | "hoa/iterator": "~1.0", | ||
1652 | "hoa/stream": "~0.0" | ||
1653 | }, | ||
1654 | "require-dev": { | ||
1655 | "hoa/test": "~1.0" | ||
1656 | }, | ||
1657 | "type": "library", | ||
1658 | "extra": { | ||
1659 | "branch-alias": { | ||
1660 | "dev-master": "0.x-dev" | ||
1661 | } | ||
1662 | }, | ||
1663 | "autoload": { | ||
1664 | "psr-4": { | ||
1665 | "Hoa\\File\\": "." | ||
1666 | } | ||
1667 | }, | ||
1668 | "notification-url": "https://packagist.org/downloads/", | ||
1669 | "license": [ | ||
1670 | "BSD-3-Clause" | ||
1671 | ], | ||
1672 | "authors": [ | ||
1673 | { | ||
1674 | "name": "Ivan Enderlin", | ||
1675 | "email": "ivan.enderlin@hoa-project.net" | ||
1676 | }, | ||
1677 | { | ||
1678 | "name": "Hoa community", | ||
1679 | "homepage": "http://hoa-project.net/" | ||
1680 | } | ||
1681 | ], | ||
1682 | "description": "The Hoa\\File library.", | ||
1683 | "homepage": "http://hoa-project.net/", | ||
1684 | "keywords": [ | ||
1685 | "Socket", | ||
1686 | "directory", | ||
1687 | "file", | ||
1688 | "finder", | ||
1689 | "library", | ||
1690 | "link", | ||
1691 | "temporary" | ||
1692 | ], | ||
1693 | "time": "2015-11-09 06:55:20" | ||
1694 | }, | ||
1695 | { | ||
1696 | "name": "hoa/iterator", | ||
1697 | "version": "1.15.10.29", | ||
1698 | "source": { | ||
1699 | "type": "git", | ||
1700 | "url": "https://github.com/hoaproject/Iterator.git", | ||
1701 | "reference": "a64ed9fd62579a34e4450134d6d1abdf77d54435" | ||
1702 | }, | ||
1703 | "dist": { | ||
1704 | "type": "zip", | ||
1705 | "url": "https://api.github.com/repos/hoaproject/Iterator/zipball/a64ed9fd62579a34e4450134d6d1abdf77d54435", | ||
1706 | "reference": "a64ed9fd62579a34e4450134d6d1abdf77d54435", | ||
1707 | "shasum": "" | ||
1708 | }, | ||
1709 | "require": { | ||
1710 | "hoa/core": "~2.0" | ||
1711 | }, | ||
1712 | "require-dev": { | ||
1713 | "hoa/test": "~1.0" | ||
1714 | }, | ||
1715 | "type": "library", | ||
1716 | "extra": { | ||
1717 | "branch-alias": { | ||
1718 | "dev-master": "1.x-dev" | ||
1719 | } | ||
1720 | }, | ||
1721 | "autoload": { | ||
1722 | "psr-4": { | ||
1723 | "Hoa\\Iterator\\": "." | ||
1724 | } | ||
1725 | }, | ||
1726 | "notification-url": "https://packagist.org/downloads/", | ||
1727 | "license": [ | ||
1728 | "BSD-3-Clause" | ||
1729 | ], | ||
1730 | "authors": [ | ||
1731 | { | ||
1732 | "name": "Ivan Enderlin", | ||
1733 | "email": "ivan.enderlin@hoa-project.net" | ||
1734 | }, | ||
1735 | { | ||
1736 | "name": "Hoa community", | ||
1737 | "homepage": "http://hoa-project.net/" | ||
1738 | } | ||
1739 | ], | ||
1740 | "description": "The Hoa\\Iterator library.", | ||
1741 | "homepage": "http://hoa-project.net/", | ||
1742 | "keywords": [ | ||
1743 | "iterator", | ||
1744 | "library" | ||
1745 | ], | ||
1746 | "time": "2015-10-29 21:37:16" | ||
1747 | }, | ||
1748 | { | ||
1749 | "name": "hoa/math", | ||
1750 | "version": "0.15.10.26", | ||
1751 | "source": { | ||
1752 | "type": "git", | ||
1753 | "url": "https://github.com/hoaproject/Math.git", | ||
1754 | "reference": "62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e" | ||
1755 | }, | ||
1756 | "dist": { | ||
1757 | "type": "zip", | ||
1758 | "url": "https://api.github.com/repos/hoaproject/Math/zipball/62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e", | ||
1759 | "reference": "62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e", | ||
1760 | "shasum": "" | ||
1761 | }, | ||
1762 | "require": { | ||
1763 | "hoa/compiler": "~2.0", | ||
1764 | "hoa/core": "~2.0", | ||
1765 | "hoa/iterator": "~1.0" | ||
1766 | }, | ||
1767 | "require-dev": { | ||
1768 | "hoa/test": "~1.0" | ||
1769 | }, | ||
1770 | "type": "library", | ||
1771 | "extra": { | ||
1772 | "branch-alias": { | ||
1773 | "dev-master": "0.x-dev" | ||
1774 | } | ||
1775 | }, | ||
1776 | "autoload": { | ||
1777 | "psr-4": { | ||
1778 | "Hoa\\Math\\": "." | ||
1779 | } | ||
1780 | }, | ||
1781 | "notification-url": "https://packagist.org/downloads/", | ||
1782 | "license": [ | ||
1783 | "BSD-3-Clause" | ||
1784 | ], | ||
1785 | "authors": [ | ||
1786 | { | ||
1787 | "name": "Ivan Enderlin", | ||
1788 | "email": "ivan.enderlin@hoa-project.net" | ||
1789 | }, | ||
1790 | { | ||
1791 | "name": "Hoa community", | ||
1792 | "homepage": "http://hoa-project.net/" | ||
1793 | } | ||
1794 | ], | ||
1795 | "description": "The Hoa\\Math library.", | ||
1796 | "homepage": "http://hoa-project.net/", | ||
1797 | "keywords": [ | ||
1798 | "arrangement", | ||
1799 | "combination", | ||
1800 | "combinatorics", | ||
1801 | "counting", | ||
1802 | "library", | ||
1803 | "math", | ||
1804 | "permutation", | ||
1805 | "sampler", | ||
1806 | "set" | ||
1807 | ], | ||
1808 | "time": "2015-10-26 15:22:52" | ||
1809 | }, | ||
1810 | { | ||
1811 | "name": "hoa/regex", | ||
1812 | "version": "0.15.08.13", | ||
1813 | "source": { | ||
1814 | "type": "git", | ||
1815 | "url": "https://github.com/hoaproject/Regex.git", | ||
1816 | "reference": "2ef8a77ef3885ca202fcd9c31a8e54c44cd04232" | ||
1817 | }, | ||
1818 | "dist": { | ||
1819 | "type": "zip", | ||
1820 | "url": "https://api.github.com/repos/hoaproject/Regex/zipball/2ef8a77ef3885ca202fcd9c31a8e54c44cd04232", | ||
1821 | "reference": "2ef8a77ef3885ca202fcd9c31a8e54c44cd04232", | ||
1822 | "shasum": "" | ||
1823 | }, | ||
1824 | "require": { | ||
1825 | "hoa/core": "~2.0", | ||
1826 | "hoa/math": "~0.0", | ||
1827 | "hoa/ustring": "~3.0", | ||
1828 | "hoa/visitor": "~1.0" | ||
1829 | }, | ||
1830 | "type": "library", | ||
1831 | "extra": { | ||
1832 | "branch-alias": { | ||
1833 | "dev-master": "0.x-dev" | ||
1834 | } | ||
1835 | }, | ||
1836 | "autoload": { | ||
1837 | "psr-4": { | ||
1838 | "Hoa\\Regex\\": "." | ||
1839 | } | ||
1840 | }, | ||
1841 | "notification-url": "https://packagist.org/downloads/", | ||
1842 | "license": [ | ||
1843 | "BSD-3-Clause" | ||
1844 | ], | ||
1845 | "authors": [ | ||
1846 | { | ||
1847 | "name": "Ivan Enderlin", | ||
1848 | "email": "ivan.enderlin@hoa-project.net" | ||
1849 | }, | ||
1850 | { | ||
1851 | "name": "Hoa community", | ||
1852 | "homepage": "http://hoa-project.net/" | ||
1853 | } | ||
1854 | ], | ||
1855 | "description": "The Hoa\\Regex library.", | ||
1856 | "homepage": "http://hoa-project.net/", | ||
1857 | "keywords": [ | ||
1858 | "compiler", | ||
1859 | "library", | ||
1860 | "regex" | ||
1861 | ], | ||
1862 | "time": "2015-08-13 06:48:47" | ||
1863 | }, | ||
1864 | { | ||
1865 | "name": "hoa/ruler", | ||
1866 | "version": "1.15.11.09", | ||
1867 | "source": { | ||
1868 | "type": "git", | ||
1869 | "url": "https://github.com/hoaproject/Ruler.git", | ||
1870 | "reference": "9afc9ae032d40b6dc10bff85c9126cf516953925" | ||
1871 | }, | ||
1872 | "dist": { | ||
1873 | "type": "zip", | ||
1874 | "url": "https://api.github.com/repos/hoaproject/Ruler/zipball/9afc9ae032d40b6dc10bff85c9126cf516953925", | ||
1875 | "reference": "9afc9ae032d40b6dc10bff85c9126cf516953925", | ||
1876 | "shasum": "" | ||
1877 | }, | ||
1878 | "require": { | ||
1879 | "hoa/compiler": "~2.0", | ||
1880 | "hoa/core": "~2.0", | ||
1881 | "hoa/file": "~0.0", | ||
1882 | "hoa/visitor": "~1.0" | ||
1883 | }, | ||
1884 | "require-dev": { | ||
1885 | "hoa/test": "~1.0" | ||
1886 | }, | ||
1887 | "type": "library", | ||
1888 | "extra": { | ||
1889 | "branch-alias": { | ||
1890 | "dev-master": "1.x-dev" | ||
1891 | } | ||
1892 | }, | ||
1893 | "autoload": { | ||
1894 | "psr-4": { | ||
1895 | "Hoa\\Ruler\\": "." | ||
1896 | } | ||
1897 | }, | ||
1898 | "notification-url": "https://packagist.org/downloads/", | ||
1899 | "license": [ | ||
1900 | "BSD-3-Clause" | ||
1901 | ], | ||
1902 | "authors": [ | ||
1903 | { | ||
1904 | "name": "Ivan Enderlin", | ||
1905 | "email": "ivan.enderlin@hoa-project.net" | ||
1906 | }, | ||
1907 | { | ||
1908 | "name": "Hoa community", | ||
1909 | "homepage": "http://hoa-project.net/" | ||
1910 | } | ||
1911 | ], | ||
1912 | "description": "The Hoa\\Ruler library.", | ||
1913 | "homepage": "http://hoa-project.net/", | ||
1914 | "keywords": [ | ||
1915 | "library", | ||
1916 | "ruler" | ||
1917 | ], | ||
1918 | "time": "2015-11-09 06:58:52" | ||
1919 | }, | ||
1920 | { | ||
1921 | "name": "hoa/stream", | ||
1922 | "version": "0.15.10.26", | ||
1923 | "source": { | ||
1924 | "type": "git", | ||
1925 | "url": "https://github.com/hoaproject/Stream.git", | ||
1926 | "reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3" | ||
1927 | }, | ||
1928 | "dist": { | ||
1929 | "type": "zip", | ||
1930 | "url": "https://api.github.com/repos/hoaproject/Stream/zipball/011ab91d942f1d7096deade4c8a10fe57d51c5b3", | ||
1931 | "reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3", | ||
1932 | "shasum": "" | ||
1933 | }, | ||
1934 | "require": { | ||
1935 | "hoa/core": "~2.0" | ||
1936 | }, | ||
1937 | "type": "library", | ||
1938 | "extra": { | ||
1939 | "branch-alias": { | ||
1940 | "dev-master": "0.x-dev" | ||
1941 | } | ||
1942 | }, | ||
1943 | "autoload": { | ||
1944 | "psr-4": { | ||
1945 | "Hoa\\Stream\\": "." | ||
1946 | } | ||
1947 | }, | ||
1948 | "notification-url": "https://packagist.org/downloads/", | ||
1949 | "license": [ | ||
1950 | "BSD-3-Clause" | ||
1951 | ], | ||
1952 | "authors": [ | ||
1953 | { | ||
1954 | "name": "Ivan Enderlin", | ||
1955 | "email": "ivan.enderlin@hoa-project.net" | ||
1956 | }, | ||
1957 | { | ||
1958 | "name": "Hoa community", | ||
1959 | "homepage": "http://hoa-project.net/" | ||
1960 | } | ||
1961 | ], | ||
1962 | "description": "The Hoa\\Stream library.", | ||
1963 | "homepage": "http://hoa-project.net/", | ||
1964 | "keywords": [ | ||
1965 | "Context", | ||
1966 | "bucket", | ||
1967 | "composite", | ||
1968 | "filter", | ||
1969 | "in", | ||
1970 | "library", | ||
1971 | "out", | ||
1972 | "protocol", | ||
1973 | "stream", | ||
1974 | "wrapper" | ||
1975 | ], | ||
1976 | "time": "2015-10-22 06:30:43" | ||
1977 | }, | ||
1978 | { | ||
1979 | "name": "hoa/ustring", | ||
1980 | "version": "3.15.11.09", | ||
1981 | "source": { | ||
1982 | "type": "git", | ||
1983 | "url": "https://github.com/hoaproject/Ustring.git", | ||
1984 | "reference": "8506be4910212b1a2beb9014763a8a4fbd871001" | ||
1985 | }, | ||
1986 | "dist": { | ||
1987 | "type": "zip", | ||
1988 | "url": "https://api.github.com/repos/hoaproject/Ustring/zipball/8506be4910212b1a2beb9014763a8a4fbd871001", | ||
1989 | "reference": "8506be4910212b1a2beb9014763a8a4fbd871001", | ||
1990 | "shasum": "" | ||
1991 | }, | ||
1992 | "require": { | ||
1993 | "hoa/core": "~2.0" | ||
1994 | }, | ||
1995 | "require-dev": { | ||
1996 | "hoa/test": "~1.0" | ||
1997 | }, | ||
1998 | "suggest": { | ||
1999 | "ext-iconv": "ext/iconv must be present (or a third implementation) to use Hoa\\Ustring::transcode().", | ||
2000 | "ext-intl": "To get a better Hoa\\Ustring::toAscii() and Hoa\\Ustring::compareTo()." | ||
2001 | }, | ||
2002 | "type": "library", | ||
2003 | "extra": { | ||
2004 | "branch-alias": { | ||
2005 | "dev-master": "3.x-dev" | ||
2006 | } | ||
2007 | }, | ||
2008 | "autoload": { | ||
2009 | "psr-4": { | ||
2010 | "Hoa\\Ustring\\": "." | ||
2011 | } | ||
2012 | }, | ||
2013 | "notification-url": "https://packagist.org/downloads/", | ||
2014 | "license": [ | ||
2015 | "BSD-3-Clause" | ||
2016 | ], | ||
2017 | "authors": [ | ||
2018 | { | ||
2019 | "name": "Ivan Enderlin", | ||
2020 | "email": "ivan.enderlin@hoa-project.net" | ||
2021 | }, | ||
2022 | { | ||
2023 | "name": "Hoa community", | ||
2024 | "homepage": "http://hoa-project.net/" | ||
2025 | } | ||
2026 | ], | ||
2027 | "description": "The Hoa\\Ustring library.", | ||
2028 | "homepage": "http://hoa-project.net/", | ||
2029 | "keywords": [ | ||
2030 | "library", | ||
2031 | "search", | ||
2032 | "string", | ||
2033 | "unicode" | ||
2034 | ], | ||
2035 | "time": "2015-11-09 06:44:33" | ||
2036 | }, | ||
2037 | { | ||
2038 | "name": "hoa/visitor", | ||
2039 | "version": "1.15.08.17", | ||
2040 | "source": { | ||
2041 | "type": "git", | ||
2042 | "url": "https://github.com/hoaproject/Visitor.git", | ||
2043 | "reference": "e30bfff741f71979f6476a41548e34afe8053c67" | ||
2044 | }, | ||
2045 | "dist": { | ||
2046 | "type": "zip", | ||
2047 | "url": "https://api.github.com/repos/hoaproject/Visitor/zipball/e30bfff741f71979f6476a41548e34afe8053c67", | ||
2048 | "reference": "e30bfff741f71979f6476a41548e34afe8053c67", | ||
2049 | "shasum": "" | ||
2050 | }, | ||
2051 | "require": { | ||
2052 | "hoa/core": "~2.0" | ||
2053 | }, | ||
2054 | "require-dev": { | ||
2055 | "hoa/test": "~1.0" | ||
2056 | }, | ||
2057 | "type": "library", | ||
2058 | "extra": { | ||
2059 | "branch-alias": { | ||
2060 | "dev-master": "1.x-dev" | ||
2061 | } | ||
2062 | }, | ||
2063 | "autoload": { | ||
2064 | "psr-4": { | ||
2065 | "Hoa\\Visitor\\": "." | ||
2066 | } | ||
2067 | }, | ||
2068 | "notification-url": "https://packagist.org/downloads/", | ||
2069 | "license": [ | ||
2070 | "BSD-3-Clause" | ||
2071 | ], | ||
2072 | "authors": [ | ||
2073 | { | ||
2074 | "name": "Ivan Enderlin", | ||
2075 | "email": "ivan.enderlin@hoa-project.net" | ||
2076 | }, | ||
2077 | { | ||
2078 | "name": "Hoa community", | ||
2079 | "homepage": "http://hoa-project.net/" | ||
2080 | } | ||
2081 | ], | ||
2082 | "description": "The Hoa\\Visitor library.", | ||
2083 | "homepage": "http://hoa-project.net/", | ||
2084 | "keywords": [ | ||
2085 | "library", | ||
2086 | "structure", | ||
2087 | "visit", | ||
2088 | "visitor" | ||
2089 | ], | ||
2090 | "time": "2015-08-17 06:30:58" | ||
2091 | }, | ||
2092 | { | ||
1488 | "name": "htmlawed/htmlawed", | 2093 | "name": "htmlawed/htmlawed", |
1489 | "version": "1.1.19", | 2094 | "version": "1.1.19", |
1490 | "source": { | 2095 | "source": { |
@@ -1532,21 +2137,21 @@ | |||
1532 | }, | 2137 | }, |
1533 | { | 2138 | { |
1534 | "name": "incenteev/composer-parameter-handler", | 2139 | "name": "incenteev/composer-parameter-handler", |
1535 | "version": "v2.1.1", | 2140 | "version": "v2.1.2", |
1536 | "source": { | 2141 | "source": { |
1537 | "type": "git", | 2142 | "type": "git", |
1538 | "url": "https://github.com/Incenteev/ParameterHandler.git", | 2143 | "url": "https://github.com/Incenteev/ParameterHandler.git", |
1539 | "reference": "84a205fe80a46101607bafbc423019527893ddd0" | 2144 | "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc" |
1540 | }, | 2145 | }, |
1541 | "dist": { | 2146 | "dist": { |
1542 | "type": "zip", | 2147 | "type": "zip", |
1543 | "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/84a205fe80a46101607bafbc423019527893ddd0", | 2148 | "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", |
1544 | "reference": "84a205fe80a46101607bafbc423019527893ddd0", | 2149 | "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", |
1545 | "shasum": "" | 2150 | "shasum": "" |
1546 | }, | 2151 | }, |
1547 | "require": { | 2152 | "require": { |
1548 | "php": ">=5.3.3", | 2153 | "php": ">=5.3.3", |
1549 | "symfony/yaml": "~2.0" | 2154 | "symfony/yaml": "~2.3|~3.0" |
1550 | }, | 2155 | }, |
1551 | "require-dev": { | 2156 | "require-dev": { |
1552 | "composer/composer": "1.0.*@dev", | 2157 | "composer/composer": "1.0.*@dev", |
@@ -1579,7 +2184,7 @@ | |||
1579 | "keywords": [ | 2184 | "keywords": [ |
1580 | "parameters management" | 2185 | "parameters management" |
1581 | ], | 2186 | ], |
1582 | "time": "2015-06-03 08:27:03" | 2187 | "time": "2015-11-10 17:04:01" |
1583 | }, | 2188 | }, |
1584 | { | 2189 | { |
1585 | "name": "j0k3r/graby", | 2190 | "name": "j0k3r/graby", |
@@ -1671,20 +2276,19 @@ | |||
1671 | }, | 2276 | }, |
1672 | { | 2277 | { |
1673 | "name": "j0k3r/php-readability", | 2278 | "name": "j0k3r/php-readability", |
1674 | "version": "v1.0.8", | 2279 | "version": "v1.0.9", |
1675 | "source": { | 2280 | "source": { |
1676 | "type": "git", | 2281 | "type": "git", |
1677 | "url": "https://github.com/j0k3r/php-readability.git", | 2282 | "url": "https://github.com/j0k3r/php-readability.git", |
1678 | "reference": "f71c3a419623f821c245e0a003edfbf2c67f278e" | 2283 | "reference": "41d7440c6e6130bacd50808342fe566e28f536fb" |
1679 | }, | 2284 | }, |
1680 | "dist": { | 2285 | "dist": { |
1681 | "type": "zip", | 2286 | "type": "zip", |
1682 | "url": "https://api.github.com/repos/j0k3r/php-readability/zipball/f71c3a419623f821c245e0a003edfbf2c67f278e", | 2287 | "url": "https://api.github.com/repos/j0k3r/php-readability/zipball/41d7440c6e6130bacd50808342fe566e28f536fb", |
1683 | "reference": "f71c3a419623f821c245e0a003edfbf2c67f278e", | 2288 | "reference": "41d7440c6e6130bacd50808342fe566e28f536fb", |
1684 | "shasum": "" | 2289 | "shasum": "" |
1685 | }, | 2290 | }, |
1686 | "require": { | 2291 | "require": { |
1687 | "ext-tidy": ">=1.2", | ||
1688 | "php": ">=5.3.3" | 2292 | "php": ">=5.3.3" |
1689 | }, | 2293 | }, |
1690 | "type": "library", | 2294 | "type": "library", |
@@ -1730,7 +2334,7 @@ | |||
1730 | "extraction", | 2334 | "extraction", |
1731 | "html" | 2335 | "html" |
1732 | ], | 2336 | ], |
1733 | "time": "2015-09-23 19:09:38" | 2337 | "time": "2015-11-10 08:55:29" |
1734 | }, | 2338 | }, |
1735 | { | 2339 | { |
1736 | "name": "j0k3r/safecurl", | 2340 | "name": "j0k3r/safecurl", |
@@ -2058,6 +2662,124 @@ | |||
2058 | "time": "2013-12-05 14:36:11" | 2662 | "time": "2013-12-05 14:36:11" |
2059 | }, | 2663 | }, |
2060 | { | 2664 | { |
2665 | "name": "kphoen/rulerz", | ||
2666 | "version": "0.14.0", | ||
2667 | "source": { | ||
2668 | "type": "git", | ||
2669 | "url": "https://github.com/K-Phoen/rulerz.git", | ||
2670 | "reference": "608649b148ffdf3437600cc0f450d59b0579148d" | ||
2671 | }, | ||
2672 | "dist": { | ||
2673 | "type": "zip", | ||
2674 | "url": "https://api.github.com/repos/K-Phoen/rulerz/zipball/608649b148ffdf3437600cc0f450d59b0579148d", | ||
2675 | "reference": "608649b148ffdf3437600cc0f450d59b0579148d", | ||
2676 | "shasum": "" | ||
2677 | }, | ||
2678 | "require": { | ||
2679 | "hoa/ruler": "~1.0", | ||
2680 | "php": ">=5.4", | ||
2681 | "symfony/property-access": "~2.3" | ||
2682 | }, | ||
2683 | "require-dev": { | ||
2684 | "behat/behat": "~3.0", | ||
2685 | "coduo/phpspec-data-provider-extension": "~1.0,!=1.0.2", | ||
2686 | "doctrine/orm": "~2.4", | ||
2687 | "elasticsearch/elasticsearch": "~1.0", | ||
2688 | "illuminate/database": "~5.0", | ||
2689 | "mikey179/vfsstream": "~1.4", | ||
2690 | "phpspec/phpspec": "~2.0", | ||
2691 | "pomm-project/cli": "~2.0@dev", | ||
2692 | "pomm-project/foundation": "~2.0@dev", | ||
2693 | "pomm-project/model-manager": "~2.0.@dev", | ||
2694 | "ruflin/elastica": "~1.0", | ||
2695 | "vlucas/phpdotenv": "~2.1" | ||
2696 | }, | ||
2697 | "suggest": { | ||
2698 | "doctrine/orm": "To execute rules as Doctrine queries", | ||
2699 | "elasticsearch/elasticsearch": "To execute rules as Elasticsearch queries", | ||
2700 | "kphoen/rulerz-spec-builder": "If you want a specification builder", | ||
2701 | "pomm-project/model-manager": "To execute rules as Pomm queries" | ||
2702 | }, | ||
2703 | "type": "library", | ||
2704 | "extra": { | ||
2705 | "branch-alias": { | ||
2706 | "dev-master": "1.0.x-dev" | ||
2707 | } | ||
2708 | }, | ||
2709 | "autoload": { | ||
2710 | "psr-4": { | ||
2711 | "RulerZ\\": "src/" | ||
2712 | } | ||
2713 | }, | ||
2714 | "notification-url": "https://packagist.org/downloads/", | ||
2715 | "license": [ | ||
2716 | "MIT" | ||
2717 | ], | ||
2718 | "authors": [ | ||
2719 | { | ||
2720 | "name": "Kévin Gomez", | ||
2721 | "email": "contact@kevingomez.fr" | ||
2722 | } | ||
2723 | ], | ||
2724 | "description": "Powerful implementation of the Specification pattern", | ||
2725 | "homepage": "https://github.com/K-Phoen/RulerZ", | ||
2726 | "keywords": [ | ||
2727 | "doctrine", | ||
2728 | "specification" | ||
2729 | ], | ||
2730 | "time": "2015-10-31 20:54:37" | ||
2731 | }, | ||
2732 | { | ||
2733 | "name": "kphoen/rulerz-bundle", | ||
2734 | "version": "0.11.0", | ||
2735 | "source": { | ||
2736 | "type": "git", | ||
2737 | "url": "https://github.com/K-Phoen/RulerZBundle.git", | ||
2738 | "reference": "dcaaed69d8252fa1e3a25802f8cf697947570778" | ||
2739 | }, | ||
2740 | "dist": { | ||
2741 | "type": "zip", | ||
2742 | "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/dcaaed69d8252fa1e3a25802f8cf697947570778", | ||
2743 | "reference": "dcaaed69d8252fa1e3a25802f8cf697947570778", | ||
2744 | "shasum": "" | ||
2745 | }, | ||
2746 | "require": { | ||
2747 | "kphoen/rulerz": "~0.1, >=0.13.0", | ||
2748 | "symfony/framework-bundle": "~2.3|~3.0", | ||
2749 | "symfony/validator": "~2.3|~3.0" | ||
2750 | }, | ||
2751 | "require-dev": { | ||
2752 | "matthiasnoback/symfony-dependency-injection-test": "~0.7", | ||
2753 | "mikey179/vfsstream": "~1.0", | ||
2754 | "phpunit/phpunit": "~4.8" | ||
2755 | }, | ||
2756 | "type": "symfony-bundle", | ||
2757 | "autoload": { | ||
2758 | "psr-4": { | ||
2759 | "KPhoen\\RulerZBundle\\": "" | ||
2760 | } | ||
2761 | }, | ||
2762 | "notification-url": "https://packagist.org/downloads/", | ||
2763 | "license": [ | ||
2764 | "MIT" | ||
2765 | ], | ||
2766 | "authors": [ | ||
2767 | { | ||
2768 | "name": "Kévin Gomez", | ||
2769 | "email": "contact@kevingomez.fr" | ||
2770 | } | ||
2771 | ], | ||
2772 | "description": "Symfony2 Bundle for RulerZ", | ||
2773 | "homepage": "https://github.com/K-Phoen/RulerZBundle", | ||
2774 | "keywords": [ | ||
2775 | "doctrine", | ||
2776 | "ruler", | ||
2777 | "rulerz", | ||
2778 | "specification" | ||
2779 | ], | ||
2780 | "time": "2015-11-13 13:00:14" | ||
2781 | }, | ||
2782 | { | ||
2061 | "name": "kriswallsmith/assetic", | 2783 | "name": "kriswallsmith/assetic", |
2062 | "version": "v1.3.1", | 2784 | "version": "v1.3.1", |
2063 | "source": { | 2785 | "source": { |
@@ -3014,25 +3736,25 @@ | |||
3014 | }, | 3736 | }, |
3015 | { | 3737 | { |
3016 | "name": "sensio/framework-extra-bundle", | 3738 | "name": "sensio/framework-extra-bundle", |
3017 | "version": "v3.0.10", | 3739 | "version": "v3.0.11", |
3018 | "source": { | 3740 | "source": { |
3019 | "type": "git", | 3741 | "type": "git", |
3020 | "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", | 3742 | "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", |
3021 | "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c" | 3743 | "reference": "a79e205737b58d557c05caef6dfa8f94d8084bca" |
3022 | }, | 3744 | }, |
3023 | "dist": { | 3745 | "dist": { |
3024 | "type": "zip", | 3746 | "type": "zip", |
3025 | "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/18fc2063c4d6569cdca47a39fbac32342eb65f3c", | 3747 | "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/a79e205737b58d557c05caef6dfa8f94d8084bca", |
3026 | "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c", | 3748 | "reference": "a79e205737b58d557c05caef6dfa8f94d8084bca", |
3027 | "shasum": "" | 3749 | "shasum": "" |
3028 | }, | 3750 | }, |
3029 | "require": { | 3751 | "require": { |
3030 | "doctrine/common": "~2.2", | 3752 | "doctrine/common": "~2.2", |
3031 | "symfony/framework-bundle": "~2.3" | 3753 | "symfony/framework-bundle": "~2.3|~3.0" |
3032 | }, | 3754 | }, |
3033 | "require-dev": { | 3755 | "require-dev": { |
3034 | "symfony/expression-language": "~2.4", | 3756 | "symfony/expression-language": "~2.4|~3.0", |
3035 | "symfony/security-bundle": "~2.4" | 3757 | "symfony/security-bundle": "~2.4|~3.0" |
3036 | }, | 3758 | }, |
3037 | "suggest": { | 3759 | "suggest": { |
3038 | "symfony/expression-language": "", | 3760 | "symfony/expression-language": "", |
@@ -3065,7 +3787,7 @@ | |||
3065 | "annotations", | 3787 | "annotations", |
3066 | "controllers" | 3788 | "controllers" |
3067 | ], | 3789 | ], |
3068 | "time": "2015-08-03 11:59:27" | 3790 | "time": "2015-10-28 15:47:04" |
3069 | }, | 3791 | }, |
3070 | { | 3792 | { |
3071 | "name": "sensiolabs/security-checker", | 3793 | "name": "sensiolabs/security-checker", |
@@ -4516,16 +5238,16 @@ | |||
4516 | }, | 5238 | }, |
4517 | { | 5239 | { |
4518 | "name": "phpunit/phpunit", | 5240 | "name": "phpunit/phpunit", |
4519 | "version": "4.8.16", | 5241 | "version": "4.8.18", |
4520 | "source": { | 5242 | "source": { |
4521 | "type": "git", | 5243 | "type": "git", |
4522 | "url": "https://github.com/sebastianbergmann/phpunit.git", | 5244 | "url": "https://github.com/sebastianbergmann/phpunit.git", |
4523 | "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e" | 5245 | "reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3" |
4524 | }, | 5246 | }, |
4525 | "dist": { | 5247 | "dist": { |
4526 | "type": "zip", | 5248 | "type": "zip", |
4527 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e", | 5249 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fa33d4ad96481b91df343d83e8c8aabed6b1dfd3", |
4528 | "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e", | 5250 | "reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3", |
4529 | "shasum": "" | 5251 | "shasum": "" |
4530 | }, | 5252 | }, |
4531 | "require": { | 5253 | "require": { |
@@ -4584,7 +5306,7 @@ | |||
4584 | "testing", | 5306 | "testing", |
4585 | "xunit" | 5307 | "xunit" |
4586 | ], | 5308 | ], |
4587 | "time": "2015-10-23 06:48:33" | 5309 | "time": "2015-11-11 11:32:49" |
4588 | }, | 5310 | }, |
4589 | { | 5311 | { |
4590 | "name": "phpunit/phpunit-mock-objects", | 5312 | "name": "phpunit/phpunit-mock-objects", |
diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php new file mode 100644 index 00000000..2cf3f808 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php | |||
@@ -0,0 +1,66 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
7 | use Symfony\Component\Console\Input\InputArgument; | ||
8 | use Symfony\Component\Console\Input\InputInterface; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | |||
11 | class TagAllCommand extends ContainerAwareCommand | ||
12 | { | ||
13 | protected function configure() | ||
14 | { | ||
15 | $this | ||
16 | ->setName('wallabag:tag:all') | ||
17 | ->setDescription('Tag all entries using the tagging rules.') | ||
18 | ->addArgument( | ||
19 | 'username', | ||
20 | InputArgument::REQUIRED, | ||
21 | 'User to tag entries for.' | ||
22 | ) | ||
23 | ; | ||
24 | } | ||
25 | |||
26 | protected function execute(InputInterface $input, OutputInterface $output) | ||
27 | { | ||
28 | try { | ||
29 | $user = $this->getUser($input->getArgument('username')); | ||
30 | } catch (NoResultException $e) { | ||
31 | $output->writeln(sprintf('<error>User %s not found.</error>', $input->getArgument('username'))); | ||
32 | |||
33 | return 1; | ||
34 | } | ||
35 | $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger'); | ||
36 | |||
37 | $output->write(sprintf('Tagging entries for user « <info>%s</info> »... ', $user->getUserName())); | ||
38 | |||
39 | $entries = $tagger->tagAllForUser($user); | ||
40 | |||
41 | $em = $this->getDoctrine()->getManager(); | ||
42 | foreach ($entries as $entry) { | ||
43 | $em->persist($entry); | ||
44 | } | ||
45 | $em->flush(); | ||
46 | |||
47 | $output->writeln('<info>Done.</info>'); | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * Fetches a user from its username. | ||
52 | * | ||
53 | * @param string $username | ||
54 | * | ||
55 | * @return \Wallabag\UserBundle\Entity\User | ||
56 | */ | ||
57 | private function getUser($username) | ||
58 | { | ||
59 | return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); | ||
60 | } | ||
61 | |||
62 | private function getDoctrine() | ||
63 | { | ||
64 | return $this->getContainer()->get('doctrine'); | ||
65 | } | ||
66 | } | ||
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 8bbe4ca0..7a187710 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php | |||
@@ -7,9 +7,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Component\HttpFoundation\JsonResponse; | 8 | use Symfony\Component\HttpFoundation\JsonResponse; |
9 | use Wallabag\CoreBundle\Entity\Config; | 9 | use Wallabag\CoreBundle\Entity\Config; |
10 | use Wallabag\CoreBundle\Entity\TaggingRule; | ||
10 | use Wallabag\UserBundle\Entity\User; | 11 | use Wallabag\UserBundle\Entity\User; |
11 | use Wallabag\CoreBundle\Form\Type\ChangePasswordType; | 12 | use Wallabag\CoreBundle\Form\Type\ChangePasswordType; |
12 | use Wallabag\CoreBundle\Form\Type\UserInformationType; | 13 | use Wallabag\CoreBundle\Form\Type\UserInformationType; |
14 | use Wallabag\CoreBundle\Form\Type\TaggingRuleType; | ||
13 | use Wallabag\CoreBundle\Form\Type\NewUserType; | 15 | use Wallabag\CoreBundle\Form\Type\NewUserType; |
14 | use Wallabag\CoreBundle\Form\Type\RssType; | 16 | use Wallabag\CoreBundle\Form\Type\RssType; |
15 | use Wallabag\CoreBundle\Tools\Utils; | 17 | use Wallabag\CoreBundle\Tools\Utils; |
@@ -98,6 +100,24 @@ class ConfigController extends Controller | |||
98 | return $this->redirect($this->generateUrl('config')); | 100 | return $this->redirect($this->generateUrl('config')); |
99 | } | 101 | } |
100 | 102 | ||
103 | // handle tagging rule | ||
104 | $taggingRule = new TaggingRule(); | ||
105 | $newTaggingRule = $this->createForm(new TaggingRuleType(), $taggingRule, array('action' => $this->generateUrl('config').'#set5')); | ||
106 | $newTaggingRule->handleRequest($request); | ||
107 | |||
108 | if ($newTaggingRule->isValid()) { | ||
109 | $taggingRule->setConfig($config); | ||
110 | $em->persist($taggingRule); | ||
111 | $em->flush(); | ||
112 | |||
113 | $this->get('session')->getFlashBag()->add( | ||
114 | 'notice', | ||
115 | 'Tagging rules updated' | ||
116 | ); | ||
117 | |||
118 | return $this->redirect($this->generateUrl('config')); | ||
119 | } | ||
120 | |||
101 | // handle adding new user | 121 | // handle adding new user |
102 | $newUser = $userManager->createUser(); | 122 | $newUser = $userManager->createUser(); |
103 | // enable created user by default | 123 | // enable created user by default |
@@ -136,6 +156,7 @@ class ConfigController extends Controller | |||
136 | 'pwd' => $pwdForm->createView(), | 156 | 'pwd' => $pwdForm->createView(), |
137 | 'user' => $userForm->createView(), | 157 | 'user' => $userForm->createView(), |
138 | 'new_user' => $newUserForm->createView(), | 158 | 'new_user' => $newUserForm->createView(), |
159 | 'new_tagging_rule' => $newTaggingRule->createView(), | ||
139 | ), | 160 | ), |
140 | 'rss' => array( | 161 | 'rss' => array( |
141 | 'username' => $user->getUsername(), | 162 | 'username' => $user->getUsername(), |
@@ -168,6 +189,33 @@ class ConfigController extends Controller | |||
168 | } | 189 | } |
169 | 190 | ||
170 | /** | 191 | /** |
192 | * Deletes a tagging rule and redirect to the config homepage. | ||
193 | * | ||
194 | * @param TaggingRule $rule | ||
195 | * | ||
196 | * @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule") | ||
197 | * | ||
198 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
199 | */ | ||
200 | public function deleteTaggingRule(TaggingRule $rule) | ||
201 | { | ||
202 | if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) { | ||
203 | throw $this->createAccessDeniedException('You can not access this tagging ryle.'); | ||
204 | } | ||
205 | |||
206 | $em = $this->getDoctrine()->getManager(); | ||
207 | $em->remove($rule); | ||
208 | $em->flush(); | ||
209 | |||
210 | $this->get('session')->getFlashBag()->add( | ||
211 | 'notice', | ||
212 | 'Tagging rule deleted' | ||
213 | ); | ||
214 | |||
215 | return $this->redirect($this->generateUrl('config')); | ||
216 | } | ||
217 | |||
218 | /** | ||
171 | * Retrieve config for the current user. | 219 | * Retrieve config for the current user. |
172 | * If no config were found, create a new one. | 220 | * If no config were found, create a new one. |
173 | * | 221 | * |
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php index cb0c52c4..84b78a89 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php | |||
@@ -6,6 +6,7 @@ use Doctrine\Common\DataFixtures\AbstractFixture; | |||
6 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; | 6 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; |
7 | use Doctrine\Common\Persistence\ObjectManager; | 7 | use Doctrine\Common\Persistence\ObjectManager; |
8 | use Wallabag\CoreBundle\Entity\Config; | 8 | use Wallabag\CoreBundle\Entity\Config; |
9 | use Wallabag\CoreBundle\Entity\TaggingRule; | ||
9 | 10 | ||
10 | class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface | 11 | class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface |
11 | { | 12 | { |
@@ -15,6 +16,13 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface | |||
15 | public function load(ObjectManager $manager) | 16 | public function load(ObjectManager $manager) |
16 | { | 17 | { |
17 | $adminConfig = new Config($this->getReference('admin-user')); | 18 | $adminConfig = new Config($this->getReference('admin-user')); |
19 | $taggingRule = new TaggingRule(); | ||
20 | |||
21 | $taggingRule->setConfig($adminConfig); | ||
22 | $taggingRule->setRule('title matches "wallabag"'); | ||
23 | $taggingRule->setTags(['wallabag']); | ||
24 | $manager->persist($taggingRule); | ||
25 | |||
18 | $adminConfig->setTheme('material'); | 26 | $adminConfig->setTheme('material'); |
19 | $adminConfig->setItemsPerPage(30); | 27 | $adminConfig->setItemsPerPage(30); |
20 | $adminConfig->setLanguage('en_US'); | 28 | $adminConfig->setLanguage('en_US'); |
diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php index b2a1915a..2ca4182e 100644 --- a/src/Wallabag/CoreBundle/Entity/Config.php +++ b/src/Wallabag/CoreBundle/Entity/Config.php | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Entity; | 3 | namespace Wallabag\CoreBundle\Entity; |
4 | 4 | ||
5 | use Doctrine\Common\Collections\ArrayCollection; | ||
5 | use Doctrine\ORM\Mapping as ORM; | 6 | use Doctrine\ORM\Mapping as ORM; |
6 | use Symfony\Component\Validator\Constraints as Assert; | 7 | use Symfony\Component\Validator\Constraints as Assert; |
7 | 8 | ||
@@ -76,12 +77,19 @@ class Config | |||
76 | */ | 77 | */ |
77 | private $user; | 78 | private $user; |
78 | 79 | ||
80 | /** | ||
81 | * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\TaggingRule", mappedBy="config", cascade={"remove"}) | ||
82 | * @ORM\OrderBy({"id" = "ASC"}) | ||
83 | */ | ||
84 | private $taggingRules; | ||
85 | |||
79 | /* | 86 | /* |
80 | * @param User $user | 87 | * @param User $user |
81 | */ | 88 | */ |
82 | public function __construct(\Wallabag\UserBundle\Entity\User $user) | 89 | public function __construct(\Wallabag\UserBundle\Entity\User $user) |
83 | { | 90 | { |
84 | $this->user = $user; | 91 | $this->user = $user; |
92 | $this->taggingRules = new ArrayCollection(); | ||
85 | } | 93 | } |
86 | 94 | ||
87 | /** | 95 | /** |
@@ -237,4 +245,24 @@ class Config | |||
237 | { | 245 | { |
238 | return $this->rssLimit; | 246 | return $this->rssLimit; |
239 | } | 247 | } |
248 | |||
249 | /** | ||
250 | * @param TaggingRule $rule | ||
251 | * | ||
252 | * @return Config | ||
253 | */ | ||
254 | public function addTaggingRule(TaggingRule $rule) | ||
255 | { | ||
256 | $this->taggingRules[] = $rule; | ||
257 | |||
258 | return $this; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * @return ArrayCollection<TaggingRule> | ||
263 | */ | ||
264 | public function getTaggingRules() | ||
265 | { | ||
266 | return $this->taggingRules; | ||
267 | } | ||
240 | } | 268 | } |
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 5aa582f8..608ed2f0 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php | |||
@@ -458,6 +458,10 @@ class Entry | |||
458 | */ | 458 | */ |
459 | public function addTag(Tag $tag) | 459 | public function addTag(Tag $tag) |
460 | { | 460 | { |
461 | if ($this->tags->contains($tag)) { | ||
462 | return; | ||
463 | } | ||
464 | |||
461 | $this->tags[] = $tag; | 465 | $this->tags[] = $tag; |
462 | $tag->addEntry($this); | 466 | $tag->addEntry($this); |
463 | } | 467 | } |
diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php new file mode 100644 index 00000000..4eab590f --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php | |||
@@ -0,0 +1,133 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Entity; | ||
4 | |||
5 | use Doctrine\ORM\Mapping as ORM; | ||
6 | use Symfony\Component\Validator\Constraints as Assert; | ||
7 | use KPhoen\RulerZBundle\Validator\Constraints as RulerZAssert; | ||
8 | |||
9 | /** | ||
10 | * Tagging rule. | ||
11 | * | ||
12 | * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\TaggingRuleRepository") | ||
13 | * @ORM\Table | ||
14 | * @ORM\Entity | ||
15 | */ | ||
16 | class TaggingRule | ||
17 | { | ||
18 | /** | ||
19 | * @var int | ||
20 | * | ||
21 | * @ORM\Column(name="id", type="integer") | ||
22 | * @ORM\Id | ||
23 | * @ORM\GeneratedValue(strategy="AUTO") | ||
24 | */ | ||
25 | private $id; | ||
26 | |||
27 | /** | ||
28 | * @var string | ||
29 | * | ||
30 | * @Assert\NotBlank() | ||
31 | * @RulerZAssert\ValidRule( | ||
32 | * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, | ||
33 | * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"} | ||
34 | * ) | ||
35 | * @ORM\Column(name="rule", type="string", nullable=false) | ||
36 | */ | ||
37 | private $rule; | ||
38 | |||
39 | /** | ||
40 | * @var array | ||
41 | * | ||
42 | * @Assert\NotBlank() | ||
43 | * @ORM\Column(name="tags", type="simple_array", nullable=false) | ||
44 | */ | ||
45 | private $tags = []; | ||
46 | |||
47 | /** | ||
48 | * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", inversedBy="taggingRules") | ||
49 | */ | ||
50 | private $config; | ||
51 | |||
52 | /** | ||
53 | * Get id. | ||
54 | * | ||
55 | * @return int | ||
56 | */ | ||
57 | public function getId() | ||
58 | { | ||
59 | return $this->id; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Set rule. | ||
64 | * | ||
65 | * @param string $rule | ||
66 | * | ||
67 | * @return TaggingRule | ||
68 | */ | ||
69 | public function setRule($rule) | ||
70 | { | ||
71 | $this->rule = $rule; | ||
72 | |||
73 | return $this; | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Get rule. | ||
78 | * | ||
79 | * @return string | ||
80 | */ | ||
81 | public function getRule() | ||
82 | { | ||
83 | return $this->rule; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Set tags. | ||
88 | * | ||
89 | * @param array<string> $tags | ||
90 | * | ||
91 | * @return TaggingRule | ||
92 | */ | ||
93 | public function setTags(array $tags) | ||
94 | { | ||
95 | $this->tags = $tags; | ||
96 | |||
97 | return $this; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Get tags. | ||
102 | * | ||
103 | * @return array<string> | ||
104 | */ | ||
105 | public function getTags() | ||
106 | { | ||
107 | return $this->tags; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Set config. | ||
112 | * | ||
113 | * @param Config $config | ||
114 | * | ||
115 | * @return TaggingRule | ||
116 | */ | ||
117 | public function setConfig(Config $config) | ||
118 | { | ||
119 | $this->config = $config; | ||
120 | |||
121 | return $this; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Get config. | ||
126 | * | ||
127 | * @return Config | ||
128 | */ | ||
129 | public function getConfig() | ||
130 | { | ||
131 | return $this->config; | ||
132 | } | ||
133 | } | ||
diff --git a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php new file mode 100644 index 00000000..23488d35 --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php | |||
@@ -0,0 +1,59 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Form\DataTransformer; | ||
4 | |||
5 | use Doctrine\Common\Persistence\ObjectManager; | ||
6 | use Symfony\Component\Form\DataTransformerInterface; | ||
7 | use Symfony\Component\Form\Exception\TransformationFailedException; | ||
8 | |||
9 | /** | ||
10 | * Transforms a comma-separated list to a proper PHP array. | ||
11 | * Example: the string "foo, bar" will become the array ["foo", "bar"] | ||
12 | */ | ||
13 | class StringToListTransformer implements DataTransformerInterface | ||
14 | { | ||
15 | /** | ||
16 | * @var string | ||
17 | */ | ||
18 | private $separator; | ||
19 | |||
20 | /** | ||
21 | * @param string $separator The separator used in the list. | ||
22 | */ | ||
23 | public function __construct($separator = ',') | ||
24 | { | ||
25 | $this->separator = $separator; | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Transforms a list to a string. | ||
30 | * | ||
31 | * @param array|null $list | ||
32 | * | ||
33 | * @return string | ||
34 | */ | ||
35 | public function transform($list) | ||
36 | { | ||
37 | if (null === $list) { | ||
38 | return ''; | ||
39 | } | ||
40 | |||
41 | return implode($this->separator, $list); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Transforms a string to a list. | ||
46 | * | ||
47 | * @param string $string | ||
48 | * | ||
49 | * @return array|null | ||
50 | */ | ||
51 | public function reverseTransform($string) | ||
52 | { | ||
53 | if ($string === null) { | ||
54 | return null; | ||
55 | } | ||
56 | |||
57 | return array_values(array_filter(array_map('trim', explode($this->separator, $string)))); | ||
58 | } | ||
59 | } | ||
diff --git a/src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php b/src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php new file mode 100644 index 00000000..7fbba38a --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php | |||
@@ -0,0 +1,38 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Form\Type; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\FormBuilderInterface; | ||
7 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
8 | |||
9 | use Wallabag\CoreBundle\Form\DataTransformer\StringToListTransformer; | ||
10 | |||
11 | class TaggingRuleType extends AbstractType | ||
12 | { | ||
13 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
14 | { | ||
15 | $builder | ||
16 | ->add('rule', 'text', array('required' => true)) | ||
17 | ->add('save', 'submit') | ||
18 | ; | ||
19 | |||
20 | $tagsField = $builder | ||
21 | ->create('tags', 'text') | ||
22 | ->addModelTransformer(new StringToListTransformer(',')); | ||
23 | |||
24 | $builder->add($tagsField); | ||
25 | } | ||
26 | |||
27 | public function configureOptions(OptionsResolver $resolver) | ||
28 | { | ||
29 | $resolver->setDefaults(array( | ||
30 | 'data_class' => 'Wallabag\CoreBundle\Entity\TaggingRule', | ||
31 | )); | ||
32 | } | ||
33 | |||
34 | public function getName() | ||
35 | { | ||
36 | return 'tagging_rule'; | ||
37 | } | ||
38 | } | ||
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index 7fb41393..3d585e61 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php | |||
@@ -3,6 +3,7 @@ | |||
3 | namespace Wallabag\CoreBundle\Helper; | 3 | namespace Wallabag\CoreBundle\Helper; |
4 | 4 | ||
5 | use Graby\Graby; | 5 | use Graby\Graby; |
6 | use Psr\Log\LoggerInterface as Logger; | ||
6 | use Wallabag\CoreBundle\Entity\Entry; | 7 | use Wallabag\CoreBundle\Entity\Entry; |
7 | use Wallabag\CoreBundle\Tools\Utils; | 8 | use Wallabag\CoreBundle\Tools\Utils; |
8 | 9 | ||
@@ -13,10 +14,14 @@ use Wallabag\CoreBundle\Tools\Utils; | |||
13 | class ContentProxy | 14 | class ContentProxy |
14 | { | 15 | { |
15 | protected $graby; | 16 | protected $graby; |
17 | protected $tagger; | ||
18 | protected $logger; | ||
16 | 19 | ||
17 | public function __construct(Graby $graby) | 20 | public function __construct(Graby $graby, RuleBasedTagger $tagger, Logger $logger) |
18 | { | 21 | { |
19 | $this->graby = $graby; | 22 | $this->graby = $graby; |
23 | $this->tagger = $tagger; | ||
24 | $this->logger = $logger; | ||
20 | } | 25 | } |
21 | 26 | ||
22 | /** | 27 | /** |
@@ -59,6 +64,15 @@ class ContentProxy | |||
59 | $entry->setPreviewPicture($content['open_graph']['og_image']); | 64 | $entry->setPreviewPicture($content['open_graph']['og_image']); |
60 | } | 65 | } |
61 | 66 | ||
67 | try { | ||
68 | $this->tagger->tag($entry); | ||
69 | } catch (\Exception $e) { | ||
70 | $this->logger->error('Error while trying to automatically tag an entry.', array( | ||
71 | 'entry_url' => $url, | ||
72 | 'error_msg' => $e->getMessage(), | ||
73 | )); | ||
74 | } | ||
75 | |||
62 | return $entry; | 76 | return $entry; |
63 | } | 77 | } |
64 | } | 78 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php new file mode 100644 index 00000000..3f9953c0 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php | |||
@@ -0,0 +1,108 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Helper; | ||
4 | |||
5 | use RulerZ\RulerZ; | ||
6 | |||
7 | use Wallabag\CoreBundle\Entity\Entry; | ||
8 | use Wallabag\CoreBundle\Entity\Tag; | ||
9 | use Wallabag\CoreBundle\Repository\EntryRepository; | ||
10 | use Wallabag\CoreBundle\Repository\TagRepository; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | |||
13 | class RuleBasedTagger | ||
14 | { | ||
15 | private $rulerz; | ||
16 | private $tagRepository; | ||
17 | private $entryRepository; | ||
18 | |||
19 | public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository) | ||
20 | { | ||
21 | $this->rulerz = $rulerz; | ||
22 | $this->tagRepository = $tagRepository; | ||
23 | $this->entryRepository = $entryRepository; | ||
24 | } | ||
25 | |||
26 | /** | ||
27 | * Add tags from rules defined by the user. | ||
28 | * | ||
29 | * @param Entry $entry Entry to tag. | ||
30 | */ | ||
31 | public function tag(Entry $entry) | ||
32 | { | ||
33 | $rules = $this->getRulesForUser($entry->getUser()); | ||
34 | |||
35 | foreach ($rules as $rule) { | ||
36 | if (!$this->rulerz->satisfies($entry, $rule->getRule())) { | ||
37 | continue; | ||
38 | } | ||
39 | |||
40 | foreach ($rule->getTags() as $label) { | ||
41 | $tag = $this->getTag($entry->getUser(), $label); | ||
42 | |||
43 | $entry->addTag($tag); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Apply all the tagging rules defined by a user on its entries. | ||
50 | * | ||
51 | * @param User $user | ||
52 | * | ||
53 | * @return array<Entry> A list of modified entries. | ||
54 | */ | ||
55 | public function tagAllForUser(User $user) | ||
56 | { | ||
57 | $rules = $this->getRulesForUser($user); | ||
58 | $entries = array(); | ||
59 | |||
60 | foreach ($rules as $rule) { | ||
61 | $qb = $this->entryRepository->getBuilderForAllByUser($user->getId()); | ||
62 | $entries = $this->rulerz->filter($qb, $rule->getRule()); | ||
63 | |||
64 | foreach ($entries as $entry) { | ||
65 | foreach ($rule->getTags() as $label) { | ||
66 | $tag = $this->getTag($user, $label); | ||
67 | |||
68 | $entry->addTag($tag); | ||
69 | $entries[] = $entry; | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return $entries; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Fetch a tag for a user. | ||
79 | * | ||
80 | * @param User $user | ||
81 | * @param string $label The tag's label. | ||
82 | * | ||
83 | * @return Tag | ||
84 | */ | ||
85 | private function getTag(User $user, $label) | ||
86 | { | ||
87 | $tag = $this->tagRepository->findOneByLabelAndUserId($label, $user->getId()); | ||
88 | |||
89 | if (!$tag) { | ||
90 | $tag = new Tag($user); | ||
91 | $tag->setLabel($label); | ||
92 | } | ||
93 | |||
94 | return $tag; | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Retrieves the tagging rules for a given user. | ||
99 | * | ||
100 | * @param User $user | ||
101 | * | ||
102 | * @return array<TaggingRule> | ||
103 | */ | ||
104 | private function getRulesForUser(User $user) | ||
105 | { | ||
106 | return $user->getConfig()->getTaggingRules(); | ||
107 | } | ||
108 | } | ||
diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php new file mode 100644 index 00000000..e6bb03b1 --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php | |||
@@ -0,0 +1,25 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Operator\Doctrine; | ||
4 | |||
5 | /** | ||
6 | * Provides a "matches" operator used for tagging rules. | ||
7 | * | ||
8 | * It asserts that a given pattern is contained in a subject, in a | ||
9 | * case-insensitive way. | ||
10 | * | ||
11 | * This operator will be used to compile tagging rules in DQL, usable | ||
12 | * by Doctrine ORM. | ||
13 | * It's registered in RulerZ using a service (wallabag.operator.doctrine.matches); | ||
14 | */ | ||
15 | class Matches | ||
16 | { | ||
17 | public function __invoke($subject, $pattern) | ||
18 | { | ||
19 | if ($pattern[0] === "'") { | ||
20 | $pattern = sprintf("'%%%s%%'", substr($pattern, 1, -1)); | ||
21 | } | ||
22 | |||
23 | return sprintf('UPPER(%s) LIKE UPPER(%s)', $subject, $pattern); | ||
24 | } | ||
25 | } | ||
diff --git a/src/Wallabag/CoreBundle/Operator/PHP/Matches.php b/src/Wallabag/CoreBundle/Operator/PHP/Matches.php new file mode 100644 index 00000000..987ed2a5 --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/PHP/Matches.php | |||
@@ -0,0 +1,21 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Operator\PHP; | ||
4 | |||
5 | /** | ||
6 | * Provides a "matches" operator used for tagging rules. | ||
7 | * | ||
8 | * It asserts that a given pattern is contained in a subject, in a | ||
9 | * case-insensitive way. | ||
10 | * | ||
11 | * This operator will be used to compile tagging rules in PHP, usable | ||
12 | * directly on Entry objects for instance. | ||
13 | * It's registered in RulerZ using a service (wallabag.operator.array.matches); | ||
14 | */ | ||
15 | class Matches | ||
16 | { | ||
17 | public function __invoke($subject, $pattern) | ||
18 | { | ||
19 | return stripos($subject, $pattern) !== false; | ||
20 | } | ||
21 | } | ||
diff --git a/src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php b/src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php new file mode 100644 index 00000000..de380738 --- /dev/null +++ b/src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php | |||
@@ -0,0 +1,9 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Repository; | ||
4 | |||
5 | use Doctrine\ORM\EntityRepository; | ||
6 | |||
7 | class TaggingRuleRepository extends EntityRepository | ||
8 | { | ||
9 | } | ||
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 8e21b052..c92b4eb3 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml | |||
@@ -53,6 +53,27 @@ services: | |||
53 | class: Wallabag\CoreBundle\Helper\ContentProxy | 53 | class: Wallabag\CoreBundle\Helper\ContentProxy |
54 | arguments: | 54 | arguments: |
55 | - @wallabag_core.graby | 55 | - @wallabag_core.graby |
56 | - @wallabag_core.rule_based_tagger | ||
57 | - @logger | ||
58 | |||
59 | wallabag_core.rule_based_tagger: | ||
60 | class: Wallabag\CoreBundle\Helper\RuleBasedTagger | ||
61 | arguments: | ||
62 | - @rulerz | ||
63 | - @wallabag_core.tag_repository | ||
64 | - @wallabag_core.entry_repository | ||
65 | |||
66 | wallabag_core.entry_repository: | ||
67 | class: Wallabag\CoreBundle\Repository\EntryRepository | ||
68 | factory: [ @doctrine.orm.default_entity_manager, getRepository ] | ||
69 | arguments: | ||
70 | - WallabagCoreBundle:Entry | ||
71 | |||
72 | wallabag_core.tag_repository: | ||
73 | class: Wallabag\CoreBundle\Repository\TagRepository | ||
74 | factory: [ @doctrine.orm.default_entity_manager, getRepository ] | ||
75 | arguments: | ||
76 | - WallabagCoreBundle:Tag | ||
56 | 77 | ||
57 | wallabag_core.registration_confirmed: | 78 | wallabag_core.registration_confirmed: |
58 | class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener | 79 | class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener |
@@ -70,3 +91,13 @@ services: | |||
70 | arguments: | 91 | arguments: |
71 | - %wallabag_url% | 92 | - %wallabag_url% |
72 | - src/Wallabag/CoreBundle/Resources/views/themes/_global/public/img/appicon/apple-touch-icon-152.png | 93 | - src/Wallabag/CoreBundle/Resources/views/themes/_global/public/img/appicon/apple-touch-icon-152.png |
94 | |||
95 | wallabag.operator.array.matches: | ||
96 | class: Wallabag\CoreBundle\Operator\PHP\Matches | ||
97 | tags: | ||
98 | - { name: rulerz.operator, executor: rulerz.executor.array, operator: matches } | ||
99 | |||
100 | wallabag.operator.doctrine.matches: | ||
101 | class: Wallabag\CoreBundle\Operator\Doctrine\Matches | ||
102 | tags: | ||
103 | - { name: rulerz.operator, executor: rulerz.executor.doctrine, operator: matches, inline: true } | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig index 7a7d6af1..cc797c63 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig | |||
@@ -145,6 +145,39 @@ | |||
145 | {{ form_rest(form.pwd) }} | 145 | {{ form_rest(form.pwd) }} |
146 | </form> | 146 | </form> |
147 | 147 | ||
148 | <h2>{% trans %}Tagging rules{% endtrans %}</h2> | ||
149 | |||
150 | <ul> | ||
151 | {% for tagging_rule in app.user.config.taggingRules %} | ||
152 | <li> | ||
153 | if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} » | ||
154 | <a href="{{ path('delete_tagging_rule', {id: tagging_rule.id}) }}" title="{% trans %}Delete{% endtrans %}" class="tool delete icon-trash icon"></a> | ||
155 | </li> | ||
156 | {% endfor %} | ||
157 | </ul> | ||
158 | |||
159 | <form action="{{ path('config') }}" method="post" {{ form_enctype(form.new_tagging_rule) }}> | ||
160 | {{ form_errors(form.new_tagging_rule) }} | ||
161 | |||
162 | <fieldset class="w500p inline"> | ||
163 | <div class="row"> | ||
164 | {{ form_label(form.new_tagging_rule.rule) }} | ||
165 | {{ form_errors(form.new_tagging_rule.rule) }} | ||
166 | {{ form_widget(form.new_tagging_rule.rule) }} | ||
167 | </div> | ||
168 | </fieldset> | ||
169 | |||
170 | <fieldset class="w500p inline"> | ||
171 | <div class="row"> | ||
172 | {{ form_label(form.new_tagging_rule.tags) }} | ||
173 | {{ form_errors(form.new_tagging_rule.tags) }} | ||
174 | {{ form_widget(form.new_tagging_rule.tags) }} | ||
175 | </div> | ||
176 | </fieldset> | ||
177 | |||
178 | {{ form_rest(form.new_tagging_rule) }} | ||
179 | </form> | ||
180 | |||
148 | {% if is_granted('ROLE_SUPER_ADMIN') %} | 181 | {% if is_granted('ROLE_SUPER_ADMIN') %} |
149 | <h2>{% trans %}Add a user{% endtrans %}</h2> | 182 | <h2>{% trans %}Add a user{% endtrans %}</h2> |
150 | 183 | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 8f121a2b..d060311d 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig | |||
@@ -15,8 +15,9 @@ | |||
15 | <li class="tab col s3"><a href="#set2">{% trans %}RSS{% endtrans %}</a></li> | 15 | <li class="tab col s3"><a href="#set2">{% trans %}RSS{% endtrans %}</a></li> |
16 | <li class="tab col s3"><a href="#set3">{% trans %}User information{% endtrans %}</a></li> | 16 | <li class="tab col s3"><a href="#set3">{% trans %}User information{% endtrans %}</a></li> |
17 | <li class="tab col s3"><a href="#set4">{% trans %}Password{% endtrans %}</a></li> | 17 | <li class="tab col s3"><a href="#set4">{% trans %}Password{% endtrans %}</a></li> |
18 | <li class="tab col s3"><a href="#set5">{% trans %}Tagging rules{% endtrans %}</a></li> | ||
18 | {% if is_granted('ROLE_SUPER_ADMIN') %} | 19 | {% if is_granted('ROLE_SUPER_ADMIN') %} |
19 | <li class="tab col s3"><a href="#set5">{% trans %}Add a user{% endtrans %}</a></li> | 20 | <li class="tab col s3"><a href="#set6">{% trans %}Add a user{% endtrans %}</a></li> |
20 | {% endif %} | 21 | {% endif %} |
21 | </ul> | 22 | </ul> |
22 | </div> | 23 | </div> |
@@ -183,8 +184,155 @@ | |||
183 | </form> | 184 | </form> |
184 | </div> | 185 | </div> |
185 | 186 | ||
186 | {% if is_granted('ROLE_SUPER_ADMIN') %} | ||
187 | <div id="set5" class="col s12"> | 187 | <div id="set5" class="col s12"> |
188 | <div class="row"> | ||
189 | <div class="input-field col s12"> | ||
190 | <ul> | ||
191 | {% for tagging_rule in app.user.config.taggingRules %} | ||
192 | <li> | ||
193 | if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} » | ||
194 | <a href="{{ path('delete_tagging_rule', {id: tagging_rule.id}) }}" title="{% trans %}Delete{% endtrans %}"> | ||
195 | <i class="tool grey-text delete mdi-action-delete"></i> | ||
196 | </a> | ||
197 | </li> | ||
198 | {% endfor %} | ||
199 | </ul> | ||
200 | </div> | ||
201 | </div> | ||
202 | |||
203 | {{ form_start(form.new_tagging_rule) }} | ||
204 | {{ form_errors(form.new_tagging_rule) }} | ||
205 | |||
206 | <div class="row"> | ||
207 | <div class="input-field col s12"> | ||
208 | {{ form_label(form.new_tagging_rule.rule) }} | ||
209 | {{ form_errors(form.new_tagging_rule.rule) }} | ||
210 | {{ form_widget(form.new_tagging_rule.rule) }} | ||
211 | </div> | ||
212 | </div> | ||
213 | |||
214 | <div class="row"> | ||
215 | <div class="input-field col s12"> | ||
216 | {{ form_label(form.new_tagging_rule.tags) }} | ||
217 | {{ form_errors(form.new_tagging_rule.tags) }} | ||
218 | {{ form_widget(form.new_tagging_rule.tags) }} | ||
219 | </div> | ||
220 | </div> | ||
221 | |||
222 | <div class="hidden">{{ form_rest(form.new_tagging_rule) }}</div> | ||
223 | <button class="btn waves-effect waves-light" type="submit" name="action"> | ||
224 | {% trans %}Save{% endtrans %} | ||
225 | </button> | ||
226 | </form> | ||
227 | |||
228 | <div class="row"> | ||
229 | <div class="input-field col s12"> | ||
230 | <h4>{% trans %}FAQ{% endtrans %}</h4> | ||
231 | |||
232 | <h5>{% trans %}What does « tagging rules » mean?{% endtrans %}</h5> | ||
233 | <p class="help"> | ||
234 | {% trans %} | ||
235 | They are rules used by Wallabag to automatically tag new entries.<br /> | ||
236 | Each time a new entry is added, all the tagging rules will be used to add | ||
237 | the tags you configured, thus saving you the trouble to manually classify | ||
238 | your entries. | ||
239 | {% endtrans %} | ||
240 | </p> | ||
241 | |||
242 | <h5>{% trans %}How do I use them?{% endtrans %}</h5> | ||
243 | <p class="help"> | ||
244 | {% trans %} | ||
245 | Let assume you want to tag new entries as « <i>short reading</i> » when the reading time is inferior to 3 minutes.<br /> | ||
246 | In that case, you should put « readingTime <= 3 » in the <i>Rule</i> field and « <i>short reading</i> » in the <i>Tags</i> | ||
247 | field.<br /> | ||
248 | Several tags can added simultaneously by separating them by a comma: « <i>short reading, must read</i> »<br /> | ||
249 | Complex rules can be written by using predefined operators: if « <i>readingTime >= 5 AND domainName = "github.com"</i> » then tag as « <i>long reading, github </i> » | ||
250 | {% endtrans %} | ||
251 | </p> | ||
252 | |||
253 | <h5>{% trans %}Which variables and operators can I use to write rules?{% endtrans %}</h5> | ||
254 | <p class="help"> | ||
255 | {% trans %}The following variables and operators can be used to create tagging rules:{% endtrans %} | ||
256 | |||
257 | <table> | ||
258 | <thead> | ||
259 | <tr> | ||
260 | <th>{% trans %}Variable{% endtrans %}</th> | ||
261 | <th>{% trans %}Meaning{% endtrans %}</th> | ||
262 | <th>{% trans %}Operator{% endtrans %}</th> | ||
263 | <th>{% trans %}Meaning{% endtrans %}</th> | ||
264 | </tr> | ||
265 | </thead> | ||
266 | |||
267 | <tbody> | ||
268 | <tr> | ||
269 | <td>title</td> | ||
270 | <td>{% trans %}Title of the entry{% endtrans %}</td> | ||
271 | <td><=</td> | ||
272 | <td>{% trans %}Less than…{% endtrans %}</td> | ||
273 | </tr> | ||
274 | <tr> | ||
275 | <td>url</td> | ||
276 | <td>{% trans %}URL of the entry{% endtrans %}</td> | ||
277 | <td><</td> | ||
278 | <td>{% trans %}Strictly less than…{% endtrans %}</td> | ||
279 | </tr> | ||
280 | <tr> | ||
281 | <td>isArchived</td> | ||
282 | <td>{% trans %}Whether the entry is archived or not{% endtrans %}</td> | ||
283 | <td>=></td> | ||
284 | <td>{% trans %}Greater than…{% endtrans %}</td> | ||
285 | </tr> | ||
286 | <tr> | ||
287 | <td>isStared</td> | ||
288 | <td>{% trans %}Whether the entry is starred or not{% endtrans %}</td> | ||
289 | <td>></td> | ||
290 | <td>{% trans %}Strictly greater than…{% endtrans %}</td> | ||
291 | </tr> | ||
292 | <tr> | ||
293 | <td>content</td> | ||
294 | <td>{% trans %}The entry's content{% endtrans %}</td> | ||
295 | <td>=</td> | ||
296 | <td>{% trans %}Equal to…{% endtrans %}</td> | ||
297 | </tr> | ||
298 | <tr> | ||
299 | <td>language</td> | ||
300 | <td>{% trans %}The entry's language{% endtrans %}</td> | ||
301 | <td>!=</td> | ||
302 | <td>{% trans %}Not equal to…{% endtrans %}</td> | ||
303 | </tr> | ||
304 | <tr> | ||
305 | <td>mimetype</td> | ||
306 | <td>{% trans %}The entry's mime-type{% endtrans %}</td> | ||
307 | <td>OR</td> | ||
308 | <td>{% trans %}One rule or another{% endtrans %}</td> | ||
309 | </tr> | ||
310 | <tr> | ||
311 | <td>readingTime</td> | ||
312 | <td>{% trans %}The estimated entry's reading time, in minutes{% endtrans %}</td> | ||
313 | <td>AND</td> | ||
314 | <td>{% trans %}One rule and another{% endtrans %}</td> | ||
315 | </tr> | ||
316 | <tr> | ||
317 | <td>domainName</td> | ||
318 | <td>{% trans %}The domain name of the entry{% endtrans %}</td> | ||
319 | <td>matches</td> | ||
320 | <td> | ||
321 | {% trans %} | ||
322 | Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br /> | ||
323 | Example: <code>title matches "football"</code> | ||
324 | {% endtrans %} | ||
325 | </td> | ||
326 | </tr> | ||
327 | </tbody> | ||
328 | </table> | ||
329 | </p> | ||
330 | </div> | ||
331 | </div> | ||
332 | </div> | ||
333 | |||
334 | {% if is_granted('ROLE_SUPER_ADMIN') %} | ||
335 | <div id="set6" class="col s12"> | ||
188 | {{ form_start(form.new_user) }} | 336 | {{ form_start(form.new_user) }} |
189 | {{ form_errors(form.new_user) }} | 337 | {{ form_errors(form.new_user) }} |
190 | 338 | ||
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php index 7085151a..7b32354f 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php | |||
@@ -479,4 +479,59 @@ class ConfigControllerTest extends WallabagCoreTestCase | |||
479 | $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text'))); | 479 | $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text'))); |
480 | $this->assertContains($expectedMessage, $alert[0]); | 480 | $this->assertContains($expectedMessage, $alert[0]); |
481 | } | 481 | } |
482 | |||
483 | public function testTaggingRuleCreation() | ||
484 | { | ||
485 | $this->logInAs('admin'); | ||
486 | $client = $this->getClient(); | ||
487 | |||
488 | $crawler = $client->request('GET', '/config'); | ||
489 | |||
490 | $this->assertTrue($client->getResponse()->isSuccessful()); | ||
491 | |||
492 | $form = $crawler->filter('button[id=tagging_rule_save]')->form(); | ||
493 | |||
494 | $data = array( | ||
495 | 'tagging_rule[rule]' => 'readingTime <= 3', | ||
496 | 'tagging_rule[tags]' => 'short reading', | ||
497 | ); | ||
498 | |||
499 | $client->submit($form, $data); | ||
500 | |||
501 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
502 | |||
503 | $crawler = $client->followRedirect(); | ||
504 | |||
505 | $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text'))); | ||
506 | $this->assertContains('Tagging rules updated', $alert[0]); | ||
507 | |||
508 | $deleteLink = $crawler->filter('.delete')->last()->link(); | ||
509 | |||
510 | $crawler = $client->click($deleteLink); | ||
511 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
512 | |||
513 | $crawler = $client->followRedirect(); | ||
514 | $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text'))); | ||
515 | $this->assertContains('Tagging rule deleted', $alert[0]); | ||
516 | } | ||
517 | |||
518 | public function dataForTaggingRuleFailed() | ||
519 | { | ||
520 | return array( | ||
521 | array( | ||
522 | array( | ||
523 | 'rss_config[rule]' => 'unknownVar <= 3', | ||
524 | 'rss_config[tags]' => 'cool tag', | ||
525 | ), | ||
526 | 'The variable « unknownVar » does not exist.', | ||
527 | ), | ||
528 | array( | ||
529 | array( | ||
530 | 'rss_config[rule]' => 'length(domainName) <= 42', | ||
531 | 'rss_config[tags]' => 'cool tag', | ||
532 | ), | ||
533 | 'The operator « length » does not exist.', | ||
534 | ), | ||
535 | ); | ||
536 | } | ||
482 | } | 537 | } |
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php index 56b4c9e4..af62aee8 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php | |||
@@ -102,6 +102,44 @@ class EntryControllerTest extends WallabagCoreTestCase | |||
102 | $this->assertContains('Google', $alert[0]); | 102 | $this->assertContains('Google', $alert[0]); |
103 | } | 103 | } |
104 | 104 | ||
105 | /** | ||
106 | * This test will require an internet connection. | ||
107 | */ | ||
108 | public function testPostNewThatWillBeTaggued() | ||
109 | { | ||
110 | $this->logInAs('admin'); | ||
111 | $client = $this->getClient(); | ||
112 | |||
113 | $crawler = $client->request('GET', '/new'); | ||
114 | |||
115 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
116 | |||
117 | $form = $crawler->filter('button[type=submit]')->form(); | ||
118 | |||
119 | $data = array( | ||
120 | 'entry[url]' => $url = 'https://github.com/wallabag/wallabag', | ||
121 | ); | ||
122 | |||
123 | $client->submit($form, $data); | ||
124 | |||
125 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
126 | |||
127 | $crawler = $client->followRedirect(); | ||
128 | |||
129 | $em = $client->getContainer() | ||
130 | ->get('doctrine.orm.entity_manager'); | ||
131 | $entry = $em | ||
132 | ->getRepository('WallabagCoreBundle:Entry') | ||
133 | ->findOneByUrl($url); | ||
134 | $tags = $entry->getTags(); | ||
135 | |||
136 | $this->assertCount(1, $tags); | ||
137 | $this->assertEquals('wallabag', $tags[0]->getLabel()); | ||
138 | |||
139 | $em->remove($entry); | ||
140 | $em->flush(); | ||
141 | } | ||
142 | |||
105 | public function testArchive() | 143 | public function testArchive() |
106 | { | 144 | { |
107 | $this->logInAs('admin'); | 145 | $this->logInAs('admin'); |
diff --git a/src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php b/src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php new file mode 100644 index 00000000..d114e5f3 --- /dev/null +++ b/src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php | |||
@@ -0,0 +1,50 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Tests\Form\DataTransformer; | ||
4 | |||
5 | use Wallabag\CoreBundle\Form\DataTransformer\StringToListTransformer; | ||
6 | |||
7 | class StringToListTransformerTest extends \PHPUnit_Framework_TestCase | ||
8 | { | ||
9 | /** | ||
10 | * @dataProvider transformProvider | ||
11 | */ | ||
12 | public function testTransformWithValidData($inputData, $expectedResult) | ||
13 | { | ||
14 | $transformer = new StringToListTransformer(); | ||
15 | |||
16 | $this->assertSame($expectedResult, $transformer->transform($inputData)); | ||
17 | } | ||
18 | |||
19 | public function transformProvider() | ||
20 | { | ||
21 | return array( | ||
22 | array( null, '' ), | ||
23 | array( array(), '' ), | ||
24 | array( array('single value'), 'single value' ), | ||
25 | array( array('first value', 'second value'), 'first value,second value' ), | ||
26 | ); | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * @dataProvider reverseTransformProvider | ||
31 | */ | ||
32 | public function testReverseTransformWithValidData($inputData, $expectedResult) | ||
33 | { | ||
34 | $transformer = new StringToListTransformer(); | ||
35 | |||
36 | $this->assertSame($expectedResult, $transformer->reverseTransform($inputData)); | ||
37 | } | ||
38 | |||
39 | public function reverseTransformProvider() | ||
40 | { | ||
41 | return array( | ||
42 | array( null, null ), | ||
43 | array( '', array() ), | ||
44 | array( 'single value', array('single value') ), | ||
45 | array( 'first value,second value', array('first value', 'second value') ), | ||
46 | array( 'first value, second value', array('first value', 'second value') ), | ||
47 | array( 'first value, , second value', array('first value', 'second value') ), | ||
48 | ); | ||
49 | } | ||
50 | } | ||
diff --git a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php index 4bce4708..ef7cbd5b 100644 --- a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php +++ b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php | |||
@@ -2,6 +2,9 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Tests\Helper; | 3 | namespace Wallabag\CoreBundle\Tests\Helper; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; | ||
6 | use Psr\Log\NullLogger; | ||
7 | |||
5 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Wallabag\CoreBundle\Entity\Entry; |
6 | use Wallabag\UserBundle\Entity\User; | 9 | use Wallabag\UserBundle\Entity\User; |
7 | use Wallabag\CoreBundle\Helper\ContentProxy; | 10 | use Wallabag\CoreBundle\Helper\ContentProxy; |
@@ -10,6 +13,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
10 | { | 13 | { |
11 | public function testWithEmptyContent() | 14 | public function testWithEmptyContent() |
12 | { | 15 | { |
16 | $tagger = $this->getTaggerMock(); | ||
17 | $tagger->expects($this->once()) | ||
18 | ->method('tag'); | ||
19 | |||
13 | $graby = $this->getMockBuilder('Graby\Graby') | 20 | $graby = $this->getMockBuilder('Graby\Graby') |
14 | ->setMethods(array('fetchContent')) | 21 | ->setMethods(array('fetchContent')) |
15 | ->disableOriginalConstructor() | 22 | ->disableOriginalConstructor() |
@@ -25,7 +32,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
25 | 'language' => '', | 32 | 'language' => '', |
26 | )); | 33 | )); |
27 | 34 | ||
28 | $proxy = new ContentProxy($graby); | 35 | $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); |
29 | $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); | 36 | $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); |
30 | 37 | ||
31 | $this->assertEquals('http://0.0.0.0', $entry->getUrl()); | 38 | $this->assertEquals('http://0.0.0.0', $entry->getUrl()); |
@@ -40,6 +47,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
40 | 47 | ||
41 | public function testWithEmptyContentButOG() | 48 | public function testWithEmptyContentButOG() |
42 | { | 49 | { |
50 | $tagger = $this->getTaggerMock(); | ||
51 | $tagger->expects($this->once()) | ||
52 | ->method('tag'); | ||
53 | |||
43 | $graby = $this->getMockBuilder('Graby\Graby') | 54 | $graby = $this->getMockBuilder('Graby\Graby') |
44 | ->setMethods(array('fetchContent')) | 55 | ->setMethods(array('fetchContent')) |
45 | ->disableOriginalConstructor() | 56 | ->disableOriginalConstructor() |
@@ -59,7 +70,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
59 | ), | 70 | ), |
60 | )); | 71 | )); |
61 | 72 | ||
62 | $proxy = new ContentProxy($graby); | 73 | $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); |
63 | $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); | 74 | $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); |
64 | 75 | ||
65 | $this->assertEquals('http://domain.io', $entry->getUrl()); | 76 | $this->assertEquals('http://domain.io', $entry->getUrl()); |
@@ -74,6 +85,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
74 | 85 | ||
75 | public function testWithContent() | 86 | public function testWithContent() |
76 | { | 87 | { |
88 | $tagger = $this->getTaggerMock(); | ||
89 | $tagger->expects($this->once()) | ||
90 | ->method('tag'); | ||
91 | |||
77 | $graby = $this->getMockBuilder('Graby\Graby') | 92 | $graby = $this->getMockBuilder('Graby\Graby') |
78 | ->setMethods(array('fetchContent')) | 93 | ->setMethods(array('fetchContent')) |
79 | ->disableOriginalConstructor() | 94 | ->disableOriginalConstructor() |
@@ -94,7 +109,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
94 | ), | 109 | ), |
95 | )); | 110 | )); |
96 | 111 | ||
97 | $proxy = new ContentProxy($graby); | 112 | $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); |
98 | $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); | 113 | $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); |
99 | 114 | ||
100 | $this->assertEquals('http://1.1.1.1', $entry->getUrl()); | 115 | $this->assertEquals('http://1.1.1.1', $entry->getUrl()); |
@@ -106,4 +121,17 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase | |||
106 | $this->assertEquals(4.0, $entry->getReadingTime()); | 121 | $this->assertEquals(4.0, $entry->getReadingTime()); |
107 | $this->assertEquals('1.1.1.1', $entry->getDomainName()); | 122 | $this->assertEquals('1.1.1.1', $entry->getDomainName()); |
108 | } | 123 | } |
124 | |||
125 | private function getTaggerMock() | ||
126 | { | ||
127 | return $this->getMockBuilder('Wallabag\CoreBundle\Helper\RuleBasedTagger') | ||
128 | ->setMethods(array('tag')) | ||
129 | ->disableOriginalConstructor() | ||
130 | ->getMock(); | ||
131 | } | ||
132 | |||
133 | private function getLogger() | ||
134 | { | ||
135 | return new NullLogger(); | ||
136 | } | ||
109 | } | 137 | } |
diff --git a/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php new file mode 100644 index 00000000..5180f7dd --- /dev/null +++ b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php | |||
@@ -0,0 +1,167 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Tests\Helper; | ||
4 | |||
5 | use Wallabag\CoreBundle\Entity\Config; | ||
6 | use Wallabag\CoreBundle\Entity\Entry; | ||
7 | use Wallabag\CoreBundle\Entity\Tag; | ||
8 | use Wallabag\CoreBundle\Entity\TaggingRule; | ||
9 | use Wallabag\UserBundle\Entity\User; | ||
10 | use Wallabag\CoreBundle\Helper\RuleBasedTagger; | ||
11 | |||
12 | class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase | ||
13 | { | ||
14 | private $rulerz; | ||
15 | private $tagRepository; | ||
16 | private $entryRepository; | ||
17 | private $tagger; | ||
18 | |||
19 | public function setUp() | ||
20 | { | ||
21 | $this->rulerz = $this->getRulerZMock(); | ||
22 | $this->tagRepository = $this->getTagRepositoryMock(); | ||
23 | $this->entryRepository = $this->getEntryRepositoryMock(); | ||
24 | |||
25 | $this->tagger = new RuleBasedTagger($this->rulerz, $this->tagRepository, $this->entryRepository); | ||
26 | } | ||
27 | |||
28 | public function testTagWithNoRule() | ||
29 | { | ||
30 | $entry = new Entry($this->getUser()); | ||
31 | |||
32 | $this->tagger->tag($entry); | ||
33 | |||
34 | $this->assertTrue($entry->getTags()->isEmpty()); | ||
35 | } | ||
36 | |||
37 | public function testTagWithNoMatchingRule() | ||
38 | { | ||
39 | $taggingRule = $this->getTaggingRule('rule as string', array('foo', 'bar')); | ||
40 | $user = $this->getUser([$taggingRule]); | ||
41 | $entry = new Entry($user); | ||
42 | |||
43 | $this->rulerz | ||
44 | ->expects($this->once()) | ||
45 | ->method('satisfies') | ||
46 | ->with($entry, 'rule as string') | ||
47 | ->willReturn(false); | ||
48 | |||
49 | $this->tagger->tag($entry); | ||
50 | |||
51 | $this->assertTrue($entry->getTags()->isEmpty()); | ||
52 | } | ||
53 | |||
54 | public function testTagWithAMatchingRule() | ||
55 | { | ||
56 | $taggingRule = $this->getTaggingRule('rule as string', array('foo', 'bar')); | ||
57 | $user = $this->getUser([$taggingRule]); | ||
58 | $entry = new Entry($user); | ||
59 | |||
60 | $this->rulerz | ||
61 | ->expects($this->once()) | ||
62 | ->method('satisfies') | ||
63 | ->with($entry, 'rule as string') | ||
64 | ->willReturn(true); | ||
65 | |||
66 | $this->tagger->tag($entry); | ||
67 | |||
68 | $this->assertFalse($entry->getTags()->isEmpty()); | ||
69 | |||
70 | $tags = $entry->getTags(); | ||
71 | $this->assertSame('foo', $tags[0]->getLabel()); | ||
72 | $this->assertSame($user, $tags[0]->getUser()); | ||
73 | $this->assertSame('bar', $tags[1]->getLabel()); | ||
74 | $this->assertSame($user, $tags[1]->getUser()); | ||
75 | } | ||
76 | |||
77 | public function testTagWithAMixOfMatchingRules() | ||
78 | { | ||
79 | $taggingRule = $this->getTaggingRule('bla bla', array('hey')); | ||
80 | $otherTaggingRule = $this->getTaggingRule('rule as string', array('foo')); | ||
81 | |||
82 | $user = $this->getUser([$taggingRule, $otherTaggingRule]); | ||
83 | $entry = new Entry($user); | ||
84 | |||
85 | $this->rulerz | ||
86 | ->method('satisfies') | ||
87 | ->will($this->onConsecutiveCalls(false, true)); | ||
88 | |||
89 | $this->tagger->tag($entry); | ||
90 | |||
91 | $this->assertFalse($entry->getTags()->isEmpty()); | ||
92 | |||
93 | $tags = $entry->getTags(); | ||
94 | $this->assertSame('foo', $tags[0]->getLabel()); | ||
95 | $this->assertSame($user, $tags[0]->getUser()); | ||
96 | } | ||
97 | |||
98 | public function testWhenTheTagExists() | ||
99 | { | ||
100 | $taggingRule = $this->getTaggingRule('rule as string', array('foo')); | ||
101 | $user = $this->getUser([$taggingRule]); | ||
102 | $entry = new Entry($user); | ||
103 | $tag = new Tag($user); | ||
104 | |||
105 | $this->rulerz | ||
106 | ->expects($this->once()) | ||
107 | ->method('satisfies') | ||
108 | ->with($entry, 'rule as string') | ||
109 | ->willReturn(true); | ||
110 | |||
111 | $this->tagRepository | ||
112 | ->expects($this->once()) | ||
113 | ->method('findOneByLabelAndUserId') | ||
114 | ->willReturn($tag); | ||
115 | |||
116 | $this->tagger->tag($entry); | ||
117 | |||
118 | $this->assertFalse($entry->getTags()->isEmpty()); | ||
119 | |||
120 | $tags = $entry->getTags(); | ||
121 | $this->assertSame($tag, $tags[0]); | ||
122 | } | ||
123 | |||
124 | private function getUser(array $taggingRules = []) | ||
125 | { | ||
126 | $user = new User(); | ||
127 | $config = new Config($user); | ||
128 | |||
129 | $user->setConfig($config); | ||
130 | |||
131 | foreach ($taggingRules as $rule) { | ||
132 | $config->addTaggingRule($rule); | ||
133 | } | ||
134 | |||
135 | return $user; | ||
136 | } | ||
137 | |||
138 | private function getTaggingRule($rule, array $tags) | ||
139 | { | ||
140 | $taggingRule = new TaggingRule(); | ||
141 | $taggingRule->setRule($rule); | ||
142 | $taggingRule->setTags($tags); | ||
143 | |||
144 | return $taggingRule; | ||
145 | } | ||
146 | |||
147 | private function getRulerZMock() | ||
148 | { | ||
149 | return $this->getMockBuilder('RulerZ\RulerZ') | ||
150 | ->disableOriginalConstructor() | ||
151 | ->getMock(); | ||
152 | } | ||
153 | |||
154 | private function getTagRepositoryMock() | ||
155 | { | ||
156 | return $this->getMockBuilder('Wallabag\CoreBundle\Repository\TagRepository') | ||
157 | ->disableOriginalConstructor() | ||
158 | ->getMock(); | ||
159 | } | ||
160 | |||
161 | private function getEntryRepositoryMock() | ||
162 | { | ||
163 | return $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository') | ||
164 | ->disableOriginalConstructor() | ||
165 | ->getMock(); | ||
166 | } | ||
167 | } | ||
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index c020f3ca..009c4881 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php | |||
@@ -23,4 +23,19 @@ class UserRepository extends EntityRepository | |||
23 | ->getQuery() | 23 | ->getQuery() |
24 | ->getOneOrNullResult(); | 24 | ->getOneOrNullResult(); |
25 | } | 25 | } |
26 | |||
27 | /** | ||
28 | * Find a user by its username. | ||
29 | * | ||
30 | * @param string $username | ||
31 | * | ||
32 | * @return User | ||
33 | */ | ||
34 | public function findOneByUserName($username) | ||
35 | { | ||
36 | return $this->createQueryBuilder('u') | ||
37 | ->andWhere('u.username = :username')->setParameter('username', $username) | ||
38 | ->getQuery() | ||
39 | ->getSingleResult(); | ||
40 | } | ||
26 | } | 41 | } |