fontyourface.module 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. <?php
  2. /**
  3. * Implements hook_permission().
  4. */
  5. function fontyourface_permission() {
  6. return array(
  7. 'administer @font-your-face' => array(
  8. 'title' => t('administer @font-your-face'),
  9. 'description' => t('Change which fonts are enabled using the admin interface.'),
  10. ),
  11. );
  12. } // fontyourface_perm
  13. /**
  14. * Implements hook_init().
  15. */
  16. function fontyourface_init() {
  17. fontyourface_add_theme_info_fonts();
  18. } // fontyourface_init
  19. /**
  20. * Load a font from the database using its URL
  21. */
  22. function fontyourface_load_font_by_url($url) {
  23. if ($font = db_query('SELECT * FROM {fontyourface_font} WHERE url LIKE :url', array(':url' => $url))->fetchObject()) {
  24. return $font;
  25. } // if
  26. return array();
  27. } // fontyourface_load_font_by_url
  28. /**
  29. * Load and display fonts that have been added through THEMENAME.info.
  30. */
  31. function fontyourface_add_theme_info_fonts() {
  32. $theme_declarations = fontyourface_parse_theme_info_file();
  33. if (isset($theme_declarations)) {
  34. foreach ($theme_declarations as $font_provider => $fonts) {
  35. if (module_exists($font_provider)) {
  36. foreach ($fonts as $declaration) {
  37. $info_function = $font_provider . '_fontyourface_info';
  38. if (function_exists($info_function)) {
  39. $provider_info = $info_function();
  40. $font_url = $provider_info['base_path'] . trim($declaration);
  41. $font = fontyourface_load_font_by_url($font_url);
  42. if (!empty($font)) {
  43. $font->css_selector = '<none>';
  44. fontyourface_font_registry($font);
  45. } // if
  46. else {
  47. if (fontyourface_count_fonts($font_provider) > 0) {
  48. drupal_set_message(t('The font declaration %declaration that is added to the theme .info file cannot be loaded. Make sure the font exists in the %provider font list.', array('%declaration' => $declaration, '%provider' => $font_provider)), 'error');
  49. } //if
  50. elseif (module_exists('fontyourface_ui')) {
  51. drupal_set_message(t('The %provider font that is added to the theme .info file cannot be loaded. This is probably because the import has not been run yet. !import to add fonts.', array('%provider' => check_plain($font_provider), '!import' => l(t('Run import'), 'admin/config/user-interface/fontyourface'))), 'error');
  52. } // elseif
  53. else {
  54. drupal_set_message(t('The %provider font that is added to the theme .info file cannot be loaded. This is probably because the import has not been run yet. Enable !module module and run import to add fonts.', array('%provider' => check_plain($font_provider), '!module' => l('@font-your-face UI', 'admin/modules', array('fragment' => 'edit-modules-font-your-face')))), 'error');
  55. } // else
  56. } // else
  57. } // if
  58. } // foreach
  59. } // if
  60. else {
  61. drupal_set_message(t('The font provider @provider is not enabled yet. Please <a href="!module_path">enable</a> it first.', array('@provider' => $font_provider, '!module_path' => url('admin/modules', array('fragment' => 'edit-modules-font-your-face')))), 'error');
  62. } // else
  63. } // foreach
  64. } // if
  65. } // fontyourface_add_theme_info_fonts
  66. /**
  67. * Returns info about the font provider for a given font.
  68. *
  69. * $font = Object: the full $font object.
  70. * $info_part = String: if this is given, we only return a string instead of an array.
  71. */
  72. function fontyourface_provider_info($font, $info_part = NULL) {
  73. $provider_info = array();
  74. if (!empty($font->provider)) {
  75. $font_provider = $font->provider;
  76. $info_function = $font_provider . '_fontyourface_info';
  77. if (function_exists($info_function)) {
  78. $provider_info = $info_function();
  79. } // if
  80. } // if
  81. return empty($info_part) ? $provider_info : $provider_info[$info_part];
  82. } // fontyourface_provider_info
  83. /**
  84. * Returns the number of fonts that exist in the database.
  85. *
  86. * @param string $provider
  87. * (optional) Filter by provider (e.g. 'google_fonts_api').
  88. * @param bool $status
  89. * (optional) Filter by status of the font (NULL = all, TRUE = enabled,
  90. * FALSE = disabled).
  91. */
  92. function fontyourface_count_fonts($provider = NULL, $status = NULL) {
  93. $query = db_select('fontyourface_font', 'fonts');
  94. $query->fields('fonts');
  95. if (!empty($provider)) {
  96. $query->condition('provider', $provider);
  97. } // if
  98. if (!empty($status)) {
  99. $query->condition('enabled', (int) $status);
  100. } // if
  101. $count = $query->execute()->rowCount();
  102. return $count;
  103. } //fontyourface_count_fonts
  104. /**
  105. * Implements template_preprocess_html().
  106. */
  107. function fontyourface_preprocess_html(&$vars) {
  108. $fonts = fontyourface_font_registry() + fontyourface_get_fonts();
  109. $css = fontyourface_generate_css($fonts);
  110. $css_md5 = md5($css);
  111. if ($css_md5 != variable_get('fontyourface_css_md5', '')) {
  112. fontyourface_rewrite_css($css);
  113. variable_set('fontyourface_css_md5', $css_md5);
  114. } // if
  115. if ($css != '') {
  116. fontyourface_add_css_in_preprocess($vars, 'fontyourface/font.css');
  117. } // if
  118. $vars['fontyourface'] = $fonts;
  119. } // fontyourface_preprocess_html
  120. /**
  121. * Generates CSS.
  122. */
  123. function fontyourface_generate_css($fonts = FALSE) {
  124. // Get font list.
  125. if (!$fonts) {
  126. $fonts = fontyourface_font_registry() + fontyourface_get_fonts();
  127. } // if
  128. // Generate CSS.
  129. $css = '';
  130. foreach ($fonts as $font) {
  131. $font_css = fontyourface_font_css($font);
  132. if ($font_css != '' && $font->css_selector != '' && $font->css_selector != '<none>') {
  133. $css .= $font->css_selector . ' { ' . $font_css . ' }' . "\n";
  134. } // if
  135. } // foreach
  136. return $css;
  137. } // fontyourface_generate_css
  138. /**
  139. * Re-writes fonts.css file.
  140. */
  141. function fontyourface_rewrite_css($css) {
  142. // Write CSS.
  143. $destination = file_stream_wrapper_uri_normalize('public://fontyourface/font.css');
  144. $destination_directory = dirname($destination);
  145. if (file_prepare_directory($destination_directory, FILE_CREATE_DIRECTORY)) {
  146. file_unmanaged_save_data($css, $destination, FILE_EXISTS_REPLACE);
  147. } // if
  148. } // fontyourface_rewrite_css
  149. /**
  150. * Adds a stylesheet in preprocess
  151. */
  152. function fontyourface_add_css_in_preprocess(&$vars, $css_path, $type = 'public') {
  153. if ($type == 'remote') {
  154. drupal_add_css($css_path, array('type' => 'external', 'group' => CSS_THEME));
  155. $vars['styles'] = drupal_get_css();
  156. } // if
  157. else {
  158. if ($type == 'public') {
  159. $css_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $css_path;
  160. } // if
  161. drupal_add_css($css_path, array('group' => CSS_THEME, 'basename' => 'fontyourface-' . md5($css_path)));
  162. $vars['styles'] = drupal_get_css();
  163. } // else
  164. } // fontyourface_add_css_in_preproce
  165. /**
  166. * Gets fonts, defaults to all enabled.
  167. */
  168. function fontyourface_get_fonts($where = "enabled = 1 AND css_selector != ''", $order_by = 'name ASC') {
  169. $fonts = array();
  170. $results = db_query('SELECT * FROM {fontyourface_font} WHERE ' . $where . ' ORDER BY ' . $order_by);
  171. while ($result = $results->fetchObject()) {
  172. fontyourface_add_font_tags($result);
  173. $fonts[] = $result;
  174. } // while
  175. return $fonts;
  176. } // fontyourface_get_fonts
  177. /**
  178. * Gets a single font.
  179. */
  180. function fontyourface_get_font($fid, $clear = FALSE) {
  181. static $fonts = array();
  182. if ($clear) {
  183. $fonts = array();
  184. } // if
  185. if (!isset($fonts[$fid])) {
  186. $fonts[$fid] = db_query('SELECT * FROM {fontyourface_font} WHERE fid = :fid', array(':fid' => $fid))->fetchObject();
  187. fontyourface_add_font_tags($fonts[$fid]);
  188. } // if
  189. return $fonts[$fid];
  190. } // fontyourface_get_font
  191. /**
  192. * Implements hook_features_api().
  193. */
  194. function fontyourface_features_api() {
  195. return array(
  196. 'fontyourface' => array(
  197. 'name' => '@font-your-face',
  198. 'file' => drupal_get_path('module', 'fontyourface') . '/fontyourface.features.inc',
  199. 'default_hook' => 'fontyourface_features_default_font',
  200. 'feature_source' => TRUE,
  201. ),
  202. );
  203. }
  204. /**
  205. * Adds tags to a font object.
  206. */
  207. function fontyourface_add_font_tags(&$font) {
  208. $font->tags = array();
  209. $query = db_select('fontyourface_tag', 't');
  210. $tag_font_alias = $query->join('fontyourface_tag_font', 'tf', 'tf.tid = t.tid');
  211. $query->condition($tag_font_alias . '.fid', $font->fid);
  212. $query->fields('t');
  213. $result = $query->execute();
  214. foreach ($result as $tag) {
  215. $font->tags[$tag->tid] = $tag->name;
  216. } // while
  217. } // fontyourface_add_tags_to_font
  218. /**
  219. * Runs the font import of the given provider. Triggered
  220. * from the UI interface and when a provider is enabled.
  221. */
  222. function fontyourface_import_fonts($provider) {
  223. if (module_exists($provider)) {
  224. $info_function = $provider . '_fontyourface_info';
  225. $provider_info = $info_function();
  226. $import_function = $provider . '_fontyourface_import';
  227. if ($import_function()) {
  228. drupal_set_message(t('@provider import complete.', array('@provider' => $provider_info['name'])));
  229. }
  230. } // if
  231. } // fontyourface_import_fonts
  232. /**
  233. * Enables a single font.
  234. */
  235. function fontyourface_enable_font(&$font) {
  236. db_update('fontyourface_font')
  237. ->fields(array('enabled' => 1))
  238. ->condition('fid', $font->fid)
  239. ->execute();
  240. $font->enabled = 1;
  241. $enable_function = $font->provider . '_fontyourface_enable';
  242. if (function_exists($enable_function)) {
  243. return $enable_function($font);
  244. } // if
  245. return TRUE;
  246. } // fontyourface_enable_font
  247. /**
  248. * Disables a single font.
  249. */
  250. function fontyourface_disable_font(&$font) {
  251. db_update('fontyourface_font')
  252. ->fields(array('enabled' => 0))
  253. ->condition('fid', $font->fid)
  254. ->execute();
  255. $font->enabled = 0;
  256. $disable_function = $font->provider . '_fontyourface_disable';
  257. if (function_exists($disable_function)) {
  258. return $disable_function($font);
  259. } // if
  260. return TRUE;
  261. } // fontyourface_disable_font
  262. /**
  263. * Sets a font's CSS selector.
  264. */
  265. function fontyourface_set_css_selector(&$font, $css_selector) {
  266. db_update('fontyourface_font')
  267. ->fields(array('css_selector' => $css_selector))
  268. ->condition('fid', $font->fid)
  269. ->execute();
  270. $font->css_selector = $css_selector;
  271. $selector_function = $font->provider . '_fontyourface_selector_update';
  272. if (function_exists($selector_function)) {
  273. return $selector_function($font);
  274. } // if
  275. } // fontyourface_set_css_selector
  276. /**
  277. * Sets a font's CSS fallbacks.
  278. */
  279. function fontyourface_set_css_fallbacks(&$font, $css_fallbacks) {
  280. db_update('fontyourface_font')
  281. ->fields(array('css_fallbacks' => $css_fallbacks))
  282. ->condition('fid', $font->fid)
  283. ->execute();
  284. $font->css_fallbacks = $css_fallbacks;
  285. } // fontyourface_set_css_fallbacks
  286. /**
  287. * Adds or updates font, depending on whether it already exists.
  288. */
  289. function fontyourface_save_font(&$font, $revert = FALSE) {
  290. $tags = array();
  291. if (!empty($font->tags)) {
  292. $tags = $font->tags;
  293. unset($font->tags);
  294. }
  295. $exists = db_query("SELECT fid FROM {fontyourface_font} WHERE url = :url", array(':url' => $font->url))->fetchObject();
  296. if ($exists) {
  297. $existing_font = fontyourface_get_font($exists->fid);
  298. $font->fid = $existing_font->fid;
  299. if (!$revert) {
  300. // Keep status, selector, and tags the same.
  301. $font->enabled = $existing_font->enabled;
  302. $font->css_selector = $existing_font->css_selector;
  303. }
  304. fontyourface_add_font_tags($font);
  305. drupal_write_record('fontyourface_font', $font, 'fid');
  306. $tags = $font->tags;
  307. } // if
  308. else {
  309. drupal_write_record('fontyourface_font', $font);
  310. } // else
  311. db_delete('fontyourface_tag_font')
  312. ->condition('fid', $font->fid)
  313. ->execute();
  314. if (!is_array($tags)) {
  315. $tags = array();
  316. } // if
  317. fontyourface_add_tags_to_font($tags, $font->fid);
  318. $font->tags = $tags;
  319. } // fontyourface_save_font
  320. /**
  321. * Adds tags to font.
  322. */
  323. function fontyourface_add_tags_to_font($tags, $fid) {
  324. foreach ($tags as $tag) {
  325. if (is_object($tag)) {
  326. $tag_object = $tag;
  327. }
  328. else{
  329. $tag_object = new StdClass;
  330. $tag_object->name = $tag;
  331. $tag_object->type = 'tag';
  332. }
  333. fontyourface_save_tag($tag_object);
  334. $tag_font = new StdClass;
  335. $tag_font->tid = $tag_object->tid;
  336. $tag_font->fid = $fid;
  337. drupal_write_record('fontyourface_tag_font', $tag_font);
  338. } // forach
  339. } // fontyourface_add_tags_to_font
  340. /**
  341. * Adds tag if it doesn't already exist.
  342. */
  343. function fontyourface_save_tag(&$tag) {
  344. $exists = db_query("SELECT tid FROM {fontyourface_tag} WHERE name = :name", array(':name' => $tag->name))->fetchObject();
  345. if ($exists) {
  346. // Check that existing tag is lowercase
  347. if (!ctype_lower($tag->name)) {
  348. $tag->name = drupal_strtolower($tag->name);
  349. }
  350. $tag->tid = $exists->tid;
  351. } // if
  352. else {
  353. $tag->name = drupal_strtolower($tag->name);
  354. drupal_write_record('fontyourface_tag', $tag);
  355. } // else
  356. } // fontyourface_save_tag
  357. /**
  358. * Gets preview from provider, if available.
  359. */
  360. function fontyourface_preview($font, $text, $size) {
  361. fontyourface_font_registry($font);
  362. $function = $font->provider . '_fontyourface_preview';
  363. if (function_exists($function)) {
  364. return $function($font, $text, $size);
  365. } // if
  366. return $text;
  367. } // fontyourface_preview
  368. /**
  369. * Gets preview text from provider, if available.
  370. */
  371. function fontyourface_short_preview_text($font) {
  372. $function = $font->provider . '_fontyourface_short_preview_text';
  373. if (function_exists($function)) {
  374. return $function($font);
  375. } // if
  376. return variable_get('fontyourface_short_preview_text', 'AaGg');
  377. } // fontyourface_short_preview_text
  378. /**
  379. * Manages registry of fonts used on current page, to provide
  380. * list to fontyourface_preprocess_html().
  381. */
  382. function fontyourface_font_registry($font = FALSE, $clear = FALSE) {
  383. static $fonts = array();
  384. if ($clear) {
  385. $fonts = array();
  386. } // if
  387. if ($font) {
  388. $fonts[$font->fid] = $font;
  389. } // if
  390. return $fonts;
  391. } // fontyourface_font_registry
  392. /**
  393. * Creates CSS with any properties set on font.
  394. */
  395. function fontyourface_font_css($font) {
  396. $css = array();
  397. if ($font->css_family) {
  398. // Enclose font family definition in single quotes if not already enclosed.
  399. if ($font->css_family[0] === "'") {
  400. $family_list = $font->css_family;
  401. } // if
  402. else {
  403. $family_list = "'" . $font->css_family . "'";
  404. } // else
  405. if ($font->css_fallbacks) {
  406. $family_list .= ', ' . $font->css_fallbacks;
  407. } // if
  408. $css[] = 'font-family: ' . $family_list . ';';
  409. } // if
  410. if ($font->css_style) {
  411. $css[] = 'font-style: ' . $font->css_style . ';';
  412. } // if
  413. if ($font->css_weight) {
  414. $css[] = 'font-weight: ' . $font->css_weight . ';';
  415. } // if
  416. return implode(' ', $css);
  417. } // fontyourface_font_c
  418. /**
  419. * Removes all fonts and related tags from given provider.
  420. */
  421. function fontyourface_provider_disable($provider) {
  422. // Delete all fonts.
  423. db_delete('fontyourface_font')
  424. ->condition('provider', $provider)
  425. ->execute();
  426. fontyourface_delete_unused_tags();
  427. } // fontyourface_provider_disable
  428. /**
  429. * Deletes all unused tags.
  430. */
  431. function fontyourface_delete_unused_tags() {
  432. // Delete all unused font-tag relationships.
  433. $missing_font_query = db_select('fontyourface_tag_font', 'tf');
  434. $font_alias = $missing_font_query->leftJoin('fontyourface_font', 'f', 'tf.fid = f.fid');
  435. $missing_fonts = $missing_font_query
  436. ->isNull($font_alias . '.fid')
  437. ->fields('tf', array('fid'))
  438. ->execute();
  439. $missing_fids = array();
  440. foreach ($missing_fonts as $missing_font) {
  441. $missing_fids[] = $missing_font->fid;
  442. } // foreach
  443. if (count($missing_fids) > 0) {
  444. db_delete('fontyourface_tag_font')
  445. ->condition('fid', $missing_fids, 'IN')
  446. ->execute();
  447. } // if
  448. // Delete all unused tags.
  449. $missing_tag_font_query = db_select('fontyourface_tag', 't');
  450. $tag_font_alias = $missing_tag_font_query->leftJoin('fontyourface_tag_font', 'tf', 'tf.tid = t.tid');
  451. $missing_tags = $missing_tag_font_query
  452. ->isNull($tag_font_alias . '.fid')
  453. ->fields('t', array('tid'))
  454. ->execute();
  455. $missing_tids = array();
  456. foreach ($missing_tags as $missing_tag) {
  457. $missing_tids[] = $missing_tag->tid;
  458. } // foreach
  459. if (count($missing_tids) > 0) {
  460. db_delete('fontyourface_tag')
  461. ->condition('tid', $missing_tids, 'IN')
  462. ->execute();
  463. } // if
  464. } // fontyourface_delete_unused_tags
  465. /**
  466. * Logs to watchdog if logging is enabled.
  467. *
  468. * @param string $message
  469. * Log message. This should be a literal string; see
  470. * http://drupal.org/node/323101 for more details.
  471. * @param array $arguments
  472. * Arguments to replace placeholders, if there are any, in $message.
  473. */
  474. function fontyourface_log($message, $arguments) {
  475. if (variable_get('fontyourface_detailed_logging', FALSE)) {
  476. watchdog('@font-your-face', $message, $arguments, WATCHDOG_INFO);
  477. } // if
  478. } // fontyourface_log
  479. /**
  480. * Parse the info file of the currently used theme
  481. */
  482. function fontyourface_parse_theme_info_file() {
  483. $font_declarations = array();
  484. $theme_default = variable_get('theme_default', 'bartik');
  485. $info = drupal_parse_info_file(drupal_get_path('theme', $theme_default) . '/' . $theme_default . '.info');
  486. if (isset($info['fonts'])) {
  487. $font_declarations = $info['fonts'];
  488. } // if
  489. return $font_declarations;
  490. } // fontyourface_parse_theme_info_file
  491. /**
  492. * Implements hook_ajax_render_alter().
  493. * Adds CSS for fonts loaded via AJAX.
  494. */
  495. function fontyourface_ajax_render_alter(&$commands) {
  496. // Clear out any non-font CSS so it doesn't get re-added.
  497. drupal_static_reset('drupal_add_css');
  498. // Get all the CSS that would otherwise be added in preprocess_html functions.
  499. $vars = array();
  500. fontyourface_preprocess_html($vars);
  501. foreach (module_implements('fontyourface_info') as $provider) {
  502. $function = $provider . '_preprocess_html';
  503. if (function_exists($function)) {
  504. $function($vars);
  505. } // if
  506. } // foreach
  507. // Add AJAX command to prepend the CSS to <head>.
  508. if (isset($vars['styles'])) {
  509. $commands[] = ajax_command_prepend('head', $vars['styles']);
  510. } // if
  511. } // fontyourface_ajax_render_alter