Skip to content

Commit

Permalink
Merge pull request #255 from fabd/feature/issue-251-improve-learned-list
Browse files Browse the repository at this point in the history
Feature/issue 251 improve learned list
  • Loading branch information
fabd authored Mar 29, 2022
2 parents b88ec8b + 355c284 commit 183327c
Show file tree
Hide file tree
Showing 94 changed files with 1,355 additions and 1,090 deletions.
16 changes: 13 additions & 3 deletions doc/Development.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,21 @@ If an image changes, simply rename it eg. `logo-20210815.gif`. For now we'd rath

## Versioning of CSS/JS not handled by Vite

Since removing Juicer, it is still possible to add versioning to JS/CSS that is not handled by Vite. There is still a mod_rewrite rule in `.htaccess` which can handle css/js files through `version/cache.php` based on file naming pattern:
It is possible to add versioning to JS/CSS that is not handled by Vite.

`/web/vendor/some-lib.min.js` => `/web/vendor/some-lib-v20210815.min.js`
There is a mod_rewrite rule in `.htaccess` which matches a filename pattern containing a version number (eg. a YYYYMMDD date), and routes these through a php scrip that sets far future expire headers.

This will cause the file to be gzipped and add far future expire headers + the client will refresh its cache if the hash changed.
RewriteRule ^(.*)\.[a-z0-9]+\.(css|js)$ /version/cache.php?file=$0 [L]

A file named...

/web/vendor/some-lib.min.js

... is referenced in the app (php/js) as:

/web/vendor/some-lib-v20210815.min.js

The `htaccess` rule will match the pattern, and get the original file, add far future expire headers, and return it as gzipped content.

# Scripts execution

Expand Down
3 changes: 3 additions & 0 deletions docker/php-apache/bash/.bash_aliases
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ alias ncu='./node_modules/.bin/ncu'
# KOOHII DEV
###############

# watch custom error log with colored output (see lib/ColorizedLogger.php)
alias kklog='tail -f koohii-log.txt'

# build / alias for favicon generator (see src/web/favicons/README.md)
alias real-favicon=./node_modules/.bin/real-favicon

Expand Down
2 changes: 2 additions & 0 deletions src/apps/koohii/config/koohiiConfiguration.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public function configure()
define('KK_ENV_DEV', CORE_ENVIRONMENT === 'dev');
define('KK_ENV_PROD', CORE_ENVIRONMENT === 'prod');

define('KK_ENV_FORK', sfConfig::get('app_fork'));

// FIXME obsolete, clean up
define('CJ_MODE', 'rtk');
define('CJ_HANZI', false);
Expand Down
118 changes: 79 additions & 39 deletions src/apps/koohii/lib/helper/BootstrapHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@
*
* COMMON HELPERS
*
* _bs_button() MUST add Bootstrap css as option 'class' => 'btn btn-success ...'
* _bs_button_with_icon() DONT add 'btn btn-success' ... MUST add 'icon' => 'fa-icon-id'
* _bs_button()
*
*
* FORM HELPERS
*
* _bs_formgroup([array $options], ...)
* _bs_formgroup([array $options], ...)
*
* $options (optional) ... passed to Symfony tag helpers
*
Expand All @@ -38,21 +37,17 @@
* Example:
* _bs_formgroup(['validate' => 'username'], ...)
*
* _bs_input_checkbox($name, $options = array())
* _bs_input_checkbox($name, $options = array())
*
* OPTIONAL 'label' => 'Label text'
* NOTE! The input's `value` is ALWAYS "1"
*
* _bs_input_text ($name, $options = array())
*
* _bs_input_textarea($name, $options = array())
*
* _bs_input_email ($name, $options = array())
*
* _bs_input_password($name, $options = array())
*
* _bs_submit_tag ($label, $options = array())
* _bs_input_email ($name, $options = array())
* _bs_input_password($name, $options = array())
* _bs_input_text ($name, $options = array())
* _bs_input_textarea($name, $options = array())
*
* _bs_submit_tag ($label, $options = array())
*
*
* FORM LAYOUT
Expand All @@ -67,41 +62,43 @@
*/

/**
* Returns html for a Bootstrap button.
* Similar to sf's button_to(), but outputs a <button> (not an <input type=button>).
*
* DOES NOT DO ANY ESCAPING of the button's contents!
*
* Uses 'btn btn-default' unless btn-success, btn-warning, etc specified.
* <b>Options:</b>
* - 'absolute' - if set to true, the helper outputs an absolute URL
* - 'query_string' - to append a query string (starting by ?) to the routed url
* - 'anchor' - to append an anchor (starting by #) to the routed url
*
* Additional options as per Symfony's link_to() helper: 'absolute', 'query_string', 'anchor', etc.
* @see lib/vendor/symfony/lib/helper/UrlHelper.php button_to()
*
* @param string $name
* @param string $internal_uri
* @param string $label valid for the button
* @param string $internal_uri 'module/action' or '@rule' of the action
* @param array $options additional HTML compliant <input> tag parameters
*/
function _bs_button($name, $internal_uri, array $options = [])
function _bs_button($label, $internal_uri, array $options = [])
{
// TODO
$html_options = _parse_attributes($options);

return link_to($name, $internal_uri, $options);
}
$url = url_for($internal_uri);

/**
* another helper to help refactoring later.
*
* Options:
* icon fontawesome icon id (eg. fa-file-o)
*
* @param mixed $name
* @param mixed $internal_uri
*/
function _bs_button_with_icon($name, $internal_uri, array $options = [])
{
$iconId = _get_option($options, 'icon');
assert($iconId !== null);
if (isset($html_options['query_string']))
{
$url = $url.'?'.$html_options['query_string'];
unset($html_options['query_string']);
}

_bs_class_merge($options, 'btn btn-success');
if (isset($html_options['anchor']))
{
$url = $url.'#'.$html_options['anchor'];
unset($html_options['anchor']);
}

$name = "<i class=\"far {$iconId}\"></i>{$name}";
$html_options['onclick'] = "document.location.href='".$url."';";
$html_options = _convert_options_to_javascript($html_options);

return link_to($name, $internal_uri, $options);
return content_tag('button', $label, $html_options);
}

// Classnames are appended to $options['class'] if present.
Expand Down Expand Up @@ -270,9 +267,23 @@ function _bs_input_textarea($name, $options = [])
return _bs_input('textarea', $name, $options);
}

/**
* Returns <input type=submit" ...> - adds 'success' button styles by
* default unless a `ko-Btn` class is used in the `class` option.
*
* @param string $label
* @param array $options Symfony tag helper options
*
* @return string
*/
function _bs_submit_tag($label, $options = [])
{
_bs_class_merge($options, 'btn btn-success');
// default to adding `success` style - unless a button class is used
$classnames = $options['class'] ?? '';
if (false === strstr($classnames, 'ko-Btn'))
{
_bs_class_merge($options, 'ko-Btn ko-Btn--success');
}

return submit_tag($label, $options);
}
Expand Down Expand Up @@ -341,3 +352,32 @@ function kk_globals_out()
echo "\n<script>\nwindow.KK || (KK = {});\n".implode("\n", $lines)."\n</script>\n";
}
}

/**
* Include FontAwesome 5 webfonts.
*
* - github (public) repo points to the free CDN version
*
* - private repo has the pro download with all icons
* (temporarily uncomment code below to enable all icons)
*
* - production should use the "subset" version compiled
* locally with the subsetter tool
*/
function include_fontawesome()
{
// TEMPORARILY uncomment this to test all pro icons (private repo)
// echo '<link href="/fonts/fa5pro/css/all-but-duo.min.css" rel="stylesheet">';
// return;

if (KK_ENV_FORK)
{
// use the free, CDN version
echo '<link href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" rel="stylesheet">';
}
else
{
// use the pro "subset" version for reduced file sizes
echo '<link href="/fonts/fa5sub/css/all.min.css" rel="stylesheet">';
}
}
3 changes: 0 additions & 3 deletions src/apps/koohii/lib/rtkUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ class rtkUser extends sfBasicSecurityUser
public const CREDENTIAL_ADMIN = 'admin';
public const CREDENTIAL_MEMBER = 'member';

// Misc. states that do not need database permanence
public const IS_RESTUDY_SESSION = 'study.restudy.start';

// misc. session attributes
public const KNOWN_KANJI = 'kanji.known';

Expand Down
40 changes: 29 additions & 11 deletions src/apps/koohii/lib/rtkValidators.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,31 @@
* an integer, the value argument is always a string coming from GET/POST requests.
*
* Validate methods:
*
*
* validateUsername($value)
* validateNoHtmlTags($value)
*
* Sanitize methods (validate & return value in the expected type or throws exception):
*
*
* sanitizeCJKUnifiedUCS($value)
*
*
*
* @author Fabrice Denis
*/

class rtkValidators
{
/**
* Validate RevTK username.
*
* @return
*
* @param object $value
* @param object $params
*
* @return
*/
public static function validateUsername($value)
{
// no special characters
$valid = (preg_match('/^[a-zA-Z0-9_]+$/', $value) > 0);

// no leet prefix, suffix or multiple non-alphanumeric characters
$valid = $valid && (preg_match('/^[0-9_]|_$|__/', $value) === 0);

Expand All @@ -40,20 +39,39 @@ public static function validateUsername($value)

public static function validateUserLocation($value)
{
return BaseValidators::validateNoHtmlTags($value) &&
BaseValidators::validateMysqlUtf8($value);
return BaseValidators::validateNoHtmlTags($value)
&& BaseValidators::validateMysqlUtf8($value);
}

/**
* Always returns a bool. Best used for checking boolean request parameters,
* to accept 'truthy' values. Less prone to break when updating forms.
*
* @param mixed $value
*
* @return bool
*/
public static function sanitizeBool($value)
{
return
true === $value
|| 'true' === $value
|| 0 !== intval($value);
}

/**
* Cast value to an int, and makes sure it checks against supported CJK range.
*
* @param mixed $value
*
* @return int
*/
public static function sanitizeCJKUnifiedUCS($value)
{
$ucs_code = BaseValidators::sanitizeInteger($value);

if (!CJK::isCJKUnifiedUCS($ucs_code)) {
if (!CJK::isCJKUnifiedUCS($ucs_code))
{
throw new sfException(__METHOD__);
}

Expand Down
7 changes: 1 addition & 6 deletions src/apps/koohii/modules/about/config/view.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ aboutSuccess:
title: 'About - Kanji Koohii'
robots: 'noindex,follow'

advertiseSuccess:
metas:
title: 'Advertise with Kanji Koohii'
robots: 'noindex,nofollow'

learnmoreSuccess:
metas:
title: 'Learn more - Kanji Koohii'

supportSuccess:
metas:
title: 'Support Kanji Koohii'
title: 'Support - Kanji Koohii'
robots: 'noindex,follow'
37 changes: 31 additions & 6 deletions src/apps/koohii/modules/about/templates/learnmore.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

## About the RTK book {#rtk}

<div class="kk-DocMain-whatisrtk padded-box rounded no-gutter-xs-sm mb-8" markdown="1">
<div class="kk-DocMain-whatisrtk ko-Box pt-6 no-gutter-xs-sm mb-8" markdown="1">

<div class="kk-RtkBook float-left mr-6 mb-4 md:px-4">
<img src="/images/3.0/help/rtk-book-cover.gif" width="137" height="205" />
Expand Down Expand Up @@ -87,6 +87,36 @@ This is done in order to provide meaningful results for Koohii users. The dictio

As such, Koohii's dictionary is not an exhaustive reference. There are already excellent resources dedicated to this such as jisho.org. The goal for Koohii's dictionary is to help you stay focused while getting through the 2000+ common use kanji.

### Restudy List {#restudy-list}

The [Restudy List](/study/failedlist) page lets you see all the kanji that are currently in your failed cards pile (the red pile on the SRS bar chart).

From here you can begin the "Restudy" process, which has two advantages:

1. helps you navigate the Restudy List, so you don't have to manually search for each kanji
2. lets you mark specific kanji as "learned", and then review just those

You can also select <span class="ko-ExBtn ko-ExBtn--danger">Review All</span> to start a SRS review of all the kanji in your failed pile.

#### Begin Restudy

Select <span class="ko-ExBtn ko-ExBtn--danger">Begin Restudy</span> to restudy, and review, parts or all of your forgotten kanji.

- After you are done re-learning a kanji (perhaps updating your story, or using another one), select <span class="ko-ExBtn ko-ExBtn--success">Add to learned list</span> below the story.
- Each time you mark a kanji as "learned" this way, you will move to the next kanji in your Restudy List, _in Heisig index order_.
- You can go back to the Restudy List page at any time and you will see the LEARNED kanji marked in the list. Select "Begin Restudy" again to continue with the remaining kanji.

**Tip!** _If you want to re-study only specific kanji in no particular order, simply click them in the Restudy List to navigate to those Study pages (and select "Add to learned list" each time)._

#### Learned Kanji

Use the Restudy process described above, to add kanji to the Learned Kanji list. "LEARNED" kanji will remain in this state until you either review the kanji succesfully (only SRS reviews), or manually clear the list.

Select <span class="ko-ExBtn ko-ExBtn--success">Review Learned</span> to start a SRS review of only those forgotten kanji that you have marked as learned. This effectively allows you to review only parts of your forgotten kanji. You may want to leave some difficult kanji in this pile indefinitely for example - to restudy only when you'll need them.

Select <span class="ko-ExBtn ko-ExBtn--danger">Clear learned list</span> to "reset" the learned kanji. You will rarely need this, but perhaps if you took a long break, and you had some "learned" kanji left over you may want to start the re-study process again.

At the end of this review (in fact, _any SRS review_), forgotten kanji will remain in the failed pile but please note _they will be cleared from the learned list_. Kanji that were succesfully reviewed (including "Hard" answer) are no longer in the Restudy List - well done!

## Using Flashcards

Expand Down Expand Up @@ -179,11 +209,6 @@ long term memory, therefore it is not possible to select the green piles. Often

</div>

### Restudy List

If you have previously forgotten kanji from SRS reviews, select **Restudy > Start**. This will take you to the first failed kanji. Once you have revised your story, select **Add to learned list**. This will take you to the next failed kanji. When you have finished working through failed kanji, select **Review** in the sidebar (top on mobile) to do a first review of these cards. Successfull reviews will move these cards into the SRS cycle, and your failed kanji list will eventually be cleared.


### Review Session

Clicking any of the stacks in the Leitner graph will take you to the reviewing screen :
Expand Down
Loading

0 comments on commit 183327c

Please sign in to comment.